28 de ago de 2014

Threads e JavaFX

Uma das coisas mais comuns que fazemos em nossa aplicação é executar algo "pesado". Por exemplo, usualmente lemos arquivos gigantes, acessamos páginas com muita informação, aguardamos a resposta do servidor, etc. Imagine que você queria fazer isso em paralelo na sua aplicação JavaFX, mas se deparada com erros. Como devemos proceder? Nesse artigo vamos falar como executar ações em paralelo na nossa aplicação sem causar transtorno aos nossos usuários.

Um caso de uso

Imagine que temos uma aplicação que mostra o código de uma página HTML dentro de um campo de texto. Para pegar o código de uma página, temos que abrir uma conexão com uma URL, ler o InputStream e em seguida atualizar o campo de texto com o resultado. Vamos as nossas soluções.

Caso 1: Abrir URL ao clique do botão

Nesse primeiro exemplo, iremos abrir a URL quando o usuário clicar no botão, veja o código:

EventHandler cenario1 = e -> {
 try {
  txtResultado.setText(carregaPagina(txtUrl.getText()));
 } catch (Exception e1) {
  e1.printStackTrace();
 }
};


Bem, esse código funciona, mas como ele trava a execução, a aplicação fica travada quando isso acontece:



Na imagem não fica nítido, mas a aplicação fica com o botão como se estivesse apertado. Como resolver isso?

Caso 2: Abrir URL em uma thread separada

Essa solução é a mais óbvia para quem já tem um pouco de intimidade com Java. O que podemos fazer é simplesmente abrir a URL em uma thread separada e pronto! A operação de abrir a URL seria em paralelo e atualização do campo de texto só feita quando terminassemos de ler a URL, veja o código:

EventHandler cenario2 = e -> {
 Thread t = new Thread(() -> {
  try {
   txtResultado.setText(carregaPagina(txtUrl.getText()));
  } catch (Exception e1) {
   e1.printStackTrace();
  }
 });
 t.start();
};

Legal, mas ao clicar algumas vezes no botão teremos o seguinte erro:

java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-3

E agora?

Threads e JavaFX

O ponto é que como JavaFX é quem coordena a modificação da parte visual da aplicação, ele não deixa que coisas feitas em outra thread tente atuar diretamente na view, assim ele tem controle da atualização da tela. Claro que os desenvolvedores da API já sabiam que fazer coisas em paralelo eram normais, assim eles arrumaram uma forma de ajudar você a criar aplicações que tenham execução de tarefas pesadas e não travar a thread do JavaFX!

Platform.runLater

Com esse método estático da classe Platform, podemos infomar ao JavaFX uma thread que será executada sob o controle dele, assim não teríamos o erro que tivemos no caso 2. Veja o nosso caso do clique:

Massa, agora não temos erro! No entanto, notem que essa thread que criamos está sob o controle do JavaFX, mas mesmo assim ela trava o botão... Como resolver isso de vez?

Task
As tasks são a solução! Com elas podemos executar algo em paralelo e pegar o resultado depois para sim atualizar a nossa aplicação. O funcionamento é simples, vamos focar em três principais métodos:


  • call: É onde fazemos nossa tarefa pesada. Esse médoto o JavaFX não está cuidando, logo não devemos alterar nada da view aqui;
  • succeded: Quando há o sucesso na execução do método call, esse método é chamado e dele podemos pegar o resultado do call usando o método getValue;
  • failed: Esse método já é chamado quando temos uma exceção na execução do método call. Nele também podemos pegar a exceção lançada com o método getExeception.
Pronto! Isso é o suficiente para começar a usar a Task, mas notem que um ponto importante é que a Task contém um tipo genérico, dessa forma temos segurança nos tipos dos dados e evitamos espalhar "casting" pelo código. Enfim, vamos ao nosso exemplo do botão com uma Task do tipo String:

EventHandler cenario4 = e -> {
 carregando.setVisible(true);
 Task tarefaCargaPg = new Task() {

  @Override
  protected String call() throws Exception {
   return carregaPagina(txtUrl.getText());
  }

  @Override
  protected void succeeded() {
   String codigo = getValue();
   carregando.setVisible(false);
   txtResultado.setText(codigo);
  }
 };
 Thread t = new Thread(tarefaCargaPg);
 t.setDaemon(true);
 t.start();
};

Ótimo! Veja nossa aplicação final abaixo. Para deixar tudo mais legal, colocamos um ProgressIndicator, assim quando a página está sendo carregada, a opção de carregar nova página fica também desabilitada.


Conclusão

Mostramos como fazer atividades em paralelo no JavaFX não é um bicho de 7 cabeças.
O nosso código está no github!

18 de ago de 2014

[Especial] Eleições 2014: Uma aplicação em JavaFX e apresentando uma API Java

Novamente esse teremos teremos eleições no nosso Brasil. Essas eleições serão especiais não só pelo fato do Brasil ser mais consciente, mas por que nunca estivemos tão conectados.
Nesse clima eu criei essa aplicação média com JavaFX que faz uso de umaAPI de transparência para mostrar dados das eleições.
Embora eu tenha achado a aplicação muito interessante, devo deixar claro que essa aplicação não tem como objetivo substituir o ótimo DivulgaCand, do TSE, mas sim divulgar o projeto do pessoal do transparencia.org e também demonstrar um pouco do que aprendemos aqui nesse blog através de uma "aplicação do mundo real".

Uma API para busca de dados das eleições

Conforme relatei em meu blog pessoal, eu já tive a honra de criar uma API REST para expor os dados das eleições. Esse ano o pessoal do transparencia.org fez uma API muito e mais abrangente do a que eu tinha feito.  O mais legal é que haverá um hackthon esse fim de semana com a API e há muitos exemplos de código e "wrappers" para facilitar o uso da mesma! Nessa postagem iremos usar um só método do Wrapper Java criado pelo Josué Eduardo

Instalando e usando o "wrapper"

A nossa aplicação utiliza Maven e Java 8. Clone o wrapper usando git e então entre no diretório da aplicação e use o comando mvn install para que ela fique disponível para outras apĺicações maven. Assim, podems adicionar o seguinte à nossa aplicação com o objetivo de acessar as classes da API java do transparência:

Agora você tem tudo para começar a codificar Java e usar os dados da eleição, mas precisa de uma chave da API. A chave pode ser adquirida seguindo as instruções no site do dev.transparencia.org.br.

Veja um exemplo de uso do wrapper para listar todos candidatos ao governo de SP:

TransparenciaClient cli = new TransparenciaClient("{Sua chave}");
cli.getCandidatos("SP", "3", null, null).forEach(c -> {
    System.out.printf("%s do partido %s para o cargo %s.\n",
                    c.getApelido(), c.getPartido(), c.getCargo());



Simples, não? Notem que no meu código eu deixei a minha chave, mas, por gentileza, não abusem ;) Conto com vocês! Deixei para facilitar para quem quer só executar a aplicação para testar.

A aplicação JavaFX

A nossa aplicação JavaFX é um projeto maven também. Você pode clonar o código e importar no netbeans. Cuidado com o repositório maven configurado no Netbeans!
Ao abrir a aplicação no netbeans e esperar ele importar as coisas certinhas, e executar a mesma com F6, você deverá ver:




Agora, vamos explicar algumas partes da aplicação e colocar links para aprofundamento do tópico envolvido:

Estrutura da aplicação

A App usa FXML e os campos do FXML são injetados no controller. É lá no controller que pegamos os dados


Os estados

Os estados sob a imagem do Brasil são Label que estão em um grupo. Na inicialização da aplicação temos um for nesses labels para registrar um listener que irá modificar o estado selecionado. Ao clicar no estado também carregamos uma imagem e o nome dele.

Os cargos

Os cargos são botões do tipo ToggleButton. Quando um novo botão do grupo é selecionado, mudamos o cargo selecionado. Cada cargo tem um código e o código é "armazenado" no botão usando o UserData.

Binding no cargo e estado selecionado

O cargo e o estado atualmente selecionados são armanezados em suas respectivas propriedades e quando há a mudança dos valores dos mesmos, iremos atualizar os dados da tabela.

A tabela e a busca de dados

Quando há a mudança do estado ou do cargo, disparamos a busca com os novos valores. Quando está acontecendo a busca, temos uma propriedade que indica que os dados estão carregando, assim disabilitamos a tabela, a seleção de cargos e mostramos um ProgressIndicator usando binding.



Paginação de dados

A API de transparência não nos deixa saber quantos candidatos temos, assim a paginação dos dados fica dificil, mas de qualquer forma, fizemos uma paginação com infinitas páginas, se não houver dados naquela página, não há erro, mas é uma requisição perdida ao servidor... Ao mudar a paginação também pedimos dados para o servidor.

Estilo

Embora a aplicação não tenha sido muito modificada, temos um pequeno CSS para o estilo de algumas coisas. Aceitamos PR se você manjar da parte visual :D

--

Enfim, a paginação e a parte de rodar uma "task" no fundo não foram tratadas nesse blog, mas serão em breve. Veja o código completo para entender melhor:


Próxima parte

Vamos voltar nesse blog com a segunda parte que é abrir uma telinha com detalhes de um candidato selecionado na tabela. Aceitaremos pull request!

Conclusão


Apresentamos uma aplicação com a qual você pode aprender JavaFX e ainda exercer sua cidadania! Mais uma vez eu peço por gentileza para não abusarem da minha chave da API :/
O código inteiro está no github  e gostaria de salientar que a aplicação inteira foi feita em algumas horas, digamos 5 horas corridas, mas isso por que a mesma teve uma mudança de planos e foi reescrita... Um agradecimento especial a minha namorada Luana que ficou programando e desenvolvendo a aplicação comigo em pleno fim de semana! :*

14 de ago de 2014

Introdução a gráficos com JavaFX

Os desenvolvedores de aplicações desktop e applets que vieram antes do JavaFX tinham muito trabalho criando gráficos.
JavaFX felizmente tem uma API pronta para criação de gráficos de diversos tipos. Nesse breve artigo vamos, através de exemplos, mostrar os gráficos mais simples do JavaFX.

A API de gráficos


As classes da API de gráficos do JavaFX ficam no pacote javafx.scene.chart. Para cada tipo de gráfico há uma classe (por exemplo, PieChart) e um tipo de objeto a ser populado com dados para serem exibidos nesse gráfico (como PieChart.Data).  Lembrando que, assim como todo componente em uma aplicação JavaFX, gráfico herda de nó.
Os principais gráficos são mostrados na imagem abaixo retirada do tutorial da Oracle.

O gráfico de Pizza

Esse é com certeza o gráfico mais fácil de usar :) Como dito, cada gráfico tem o seu tipo de dados. Os tipos de dados do gráfico de pizza é o PieChart.Data  e esse tipo de dado contém uma String, que é o nome do dado, e um valor double, que é o valor desse dados.
Os valores são somados e dividos pela quantidade de dados, assim descobrimos qual a fatia de cado um.
Veja como é fácil criar um gráfico com dados fictícios de uma empresa com os ganhos de cada trimestre do anos:

PieChart graficoPizza = new PieChart();
graficoPizza.getData().addAll(new PieChart.Data("Trimestre 1", 11),
  new PieChart.Data("Trimestre 2", 1),
  new PieChart.Data("Trimestre 3", 34),
  new PieChart.Data("Trimestre 5", 12));
graficoPizza.setTitle("Lucros por Trimestre");
graficoPizza.setPrefSize(GRAFICO_LARGURA, GRAFICO_ALTURA);

Viu! O que temos acima é:

  • PieChart: Um nó que representa o gráfico a ser desenhado;
  • PieChart.Data: os dados do gráfico;
Por fim podemos adicionar o gráfico a uma cena e teremos o seguinte resultado:


O gráfico de linha


O gráfico de linha é representado pela classe LineChart e não é dificil de ser usado também, no entanto, os dados já não são tão simples de criar. Ele é um subtipo de gráfico com plano cartesiano X e Y, ou seja, XYChart e o tipo de dados para esse gráfico são o XYChart.Series. Mas o que vem a ser esse tipo de dados?
Esse tipo de dado consiste principalmente de valores para X, valores para Y de um dado grupo de dados. Um exemplo seria comparar o lucro de cada produto de uma empresa ao longo do ano.
O gráfico de linha, no entanto, também exige a definição dos eixos X e Y através da classe Axis.
Bem, vejamos um exemplo de comparação de lucros por trimestre da empresa separado por produtos:

LineChart graficoLinha = new LineChart<>(
  new CategoryAxis(), new NumberAxis());
final String T1 = "T1";
final String T2 = "T2";
final String T3 = "T3";
final String T4 = "T4";

XYChart.Series prod1 = new XYChart.Series();
prod1.setName("Produto 1");

prod1.getData().add(new XYChart.Data(T1, 5));
prod1.getData().add(new XYChart.Data(T2, -2));
prod1.getData().add(new XYChart.Data(T3, 3));
prod1.getData().add(new XYChart.Data(T4, 15));

XYChart.Series prod2 = new XYChart.Series();
prod2.setName("Produto 2");

prod2.getData().add(new XYChart.Data(T1, -5));
prod2.getData().add(new XYChart.Data(T2, -1));
prod2.getData().add(new XYChart.Data(T3, 12));
prod2.getData().add(new XYChart.Data(T4, 8));

XYChart.Series prod3 = new XYChart.Series();
prod3.setName("Produto 3");

prod3.getData().add(new XYChart.Data(T1, 12));
prod3.getData().add(new XYChart.Data(T2, 15));
prod3.getData().add(new XYChart.Data(T3, 12));
prod3.getData().add(new XYChart.Data(T4, 20));
graficoLinha.getData().addAll(prod1, prod2, prod3);
graficoLinha.setPrefSize(GRAFICO_LARGURA, GRAFICO_ALTURA);

Isso geraria:



Notem que o código consiste de agrupamento de dados em tipo de categoria. No nosso caso agrupamos os dados do Produto 1 e a categoria foi determinada pelo trimestre.

O gráfico de Barras

Muito similar ao gráfico de linha, temos o gráfico de barras. Esse gráfico usa o mesmo tipo de dados, mas representa de forma: através de barras. Veja como o exemplo anterior, de linhas, usando o mesmo conjunto de dados, mas em um gráfico de barras:

BarChart graficoBarra = new BarChart<>(
  new CategoryAxis(), new NumberAxis());
// igualzinho ao feito para o gráfico de linha...
graficoBarra.getData().addAll(prod1, prod2, prod3);
graficoBarra.setPrefSize(GRAFICO_LARGURA, GRAFICO_ALTURA);



Conclusão

Introduzimos os gráficos do JavaFX! Bem, a API é extensa, voltaremos ao assunto em breve falando de outros gráficos. Até lá! Por enquanto, veja os gráficos na documentação oficial do JavaFX.

Código no Github.

11 de ago de 2014

API do JavaFX: Properties, Listeners e Bindings

Uma das características mais interessantes do JavaFX também está na API não gráfica, onde podemos facilitar com que elementos de interface respondam a variações em classes Java, usualmente classes que representam um negócio. Hoje vamos falar das classes de Properties do JavaFX e dar pequenos exemplos para entendimento das mesmas.

Como fazer com que a mudança de dados reflita na aplicação?

Imagine que sua aplicação você tem um campo de entrada texto, um TextField, e em seguida um texto, um Label, para mostrar o que tem nesse campo, em tempo real, como você faria?
  
Bem, a forma mais usual é adicionar um key handler ao campo de texto e quando esse for acionado, configurar o texto do Label. Veja o código:

TextField txtNome = new TextField();
Label lblNome = new Label();
txtNome.setOnKeyReleased(e -> {
 lblNome.setText(txtNome.getText());
});
lblNome.setFont(Font.font("Verdana", FontWeight.BOLD, 30));


Esse cenário, no entanto, pode ser muito mais complexo e levar você a ter que espalhar listeners na aplicação para fazer com que duas propriedades reflitam uma a outra e, o pior de tudo, o código pode não ficar tão legível e de difícil manutenção.

Properties


Felizmente o JavaFX tem uma API de properties, onde foram criadas diversas classes que adicionam "superpoderes" aos tipos mais comuns do java e que podem ser extensíveis para usarmos com nosso objetos da aplicação.
O melhor ainda é que toda a API do JavaFX está preparada para isso. Todos os campos de um elemento do JavaFX, como o Node, têm um equivalente com property. Por exemplo, um campo de texto tem um textProperty para o valor de texto do mesmo.

Binding

Um super poder muito notável das properties é o binding. Realizar binding significa fazer com que uma propriedade tenha seu valor modificado em função da outra. Exemplificando, se temos o texto A e queremos que o texto B seja o mesmo que o A, mas mais ainda, que B atualize quando B atualizar, dizemos que B está amarrado à A (bound). Quando isso acontece, B não pode ser modificado diretamente ou teremos um erro. Se quiser mudar B, teremos que mudar A. Isso em código fica como mostrado a seguir (reescrevendo nosso exemplo anterior):

TextField txtNome = new TextField();
Label lblNome = new Label();
lblNome.textProperty().bind(txtNome.textProperty());  
lblNome.setFont(Font.font("Verdana", FontWeight.BOLD, 30)); 

Simples, não? Aqui pode não parecer tão intessante, mas imagine que estamos em uma aplicação que traz objetos do banco de dados e queremos refletir os dados desses objetos na view em tempo real. Não seria essa uma boa alternativa? Adicionalmente podemos realizar um binding bidirecional, onde se au altero A, B também será alterado, e se eu altero B, A também terá o conteúdo de B!

Note que por enquanto falamos somente de texto, mas isso é possível com valores boleanos, inteiros e do tipo double:

StringProperty: Para textos (textProperty no TextField ou no Label)
BooleanProperty: Valores booleanos( visibleProperty, disableProperty no Node)
DoubleProperty: Valores double (value no Slider)
IntegerProperty: Valores inteiros (int)
ObjectProperty: Objetos no geral.


Cada uma das properties acima tem um equivalente para ser instanciado caso você queria usar as properties em sua aplicação, por exemplo: SimpleStringProperty, SimpleBooleanProperty, etc..
Agora, para introduzir a API, vamos mostrar brevemente tudo isso. Futuramente voltamos a aprofundar nesse tópico.

Binding com expressões

Imagine agora que você queira não só amarrar um texto, mas amarrar um texto com outro e ainda um texto fixo no meio, como fazer? Ou, se estiver usando números, quer que um valor seja sempre o resultado da soma desses dois números. Isso é muitp fácil e é possível através das expressões das properties.
Por exemplo, para  String, temos a expressão concat,  onde passamos passar outra String literal ou outra String property e cancat irá retornar uma StringProperty que é atualizada com o resultado daquela concatenação, veja um simples exemplo:





TextField txtNome = new TextField();
TextField txtSaudacao = new TextField();
Label lblNome = new Label();
lblNome.textProperty().bind(
  txtNome.textProperty().concat(", ")
  .concat(txtSaudacao.textProperty()
));



Claro que nem sempre queremos que o resultado dessa expressão seja outro texto, logo, temos também expressões da String que retornam uma propriedade de outro tipo. Por exemplo, a expressão greaterThanOrEqualTo irá retornar uma BooleanProperty de comparação de duas StringProperty. Um outro exemplo é gerar um resultando de soma de duas DoubleProperty e uma String que contenha a representação em texto, veja:







Slider sld1 = new Slider(0, 100, 50);
Slider sld2 = new Slider(0, 100, 50);
Label lblResultado = new Label();
lblResultado.textProperty().bind(
  sld1.valueProperty()
  .add(sld2.valueProperty())
  .asString()
);



Bem, paramos por aqui, mas a javadoc é sua amiga. Leia por lá e navegue na API que em breve você está mestre nisso!

Observando mudanças


Lembra do padrão observer? Um super poder das properties é também deixar que você observe ela. Assim, quando ela muda temos um pedaço de código notificado. Esse código notificado irá receber três parâmetros: o valor observável que sofreu a mudança, o valor que era antes, o novo valor. Simples, não? Com Java 8 é ainda mais simples pois podemos usar Lambdas para definir os ChangeListeners. Veja o seguinte exemplo que observamos as mudanças de um combo box:



String valores[] = { "Garrafa", "Copo", "Monitor", "Notebook",
  "Celular" };
// na próxima falamos de FXCollections
ComboBox cmbValores = new ComboBox(
  FXCollections.observableArrayList(valores));
Label lblAntigo = new Label();
Label lblNovo = new Label();
cmbValores.valueProperty().addListener((obs, velho, novo) -> {
 lblAntigo.setText("Valor Antigo: " + velho);
 lblNovo.setText("Valor Novo: " + novo);
});


Conclusão

Ah, chegamos ao fim... Essa API é muito abrangente e divertida, por isso iremos voltar ao tópico no futuro. Veja o código da nossa aplicação de teste no github e a carinha dela final: