18 de set. de 2013

Criando animações usando as transições do JavaFX 2 - Prática

Seguindo o artigo anterior, hoje vamos mostrar as transições na prática, mostrar um pouco de código!
Para demonstrar a ideia básica dessa API, criei uma aplicação e fiz um vídeo, vejam abaixo:



Código

Para criar as transições, usamos uma classe chamada FabricaTransicao  Nessa classe há um método chamado fazerTransicao que, dado um enum, duração e um nó, sempre cria uma nova transição:

public static class FabricaTransicao {

 public static enum Transicoes {
  FADE, TRANSLATE, SCALE, FILL, ROTATE
 }

 public static Transition fazerTransicao(Transicoes transicao,
   double duracaoSegundos, Node alvo) {
  Duration duracao = new Duration(duracaoSegundos * 1000);
  Transition t = null;

  switch (transicao) {
  case FADE:
   FadeTransition fadeTransition = new FadeTransition();
   fadeTransition.setFromValue(1);
   fadeTransition.setToValue(0);
   fadeTransition.setDuration(duracao);
   fadeTransition.setNode(alvo);
   t = fadeTransition;
   break;
  case FILL:
   FillTransition fillTransition = new FillTransition();
   fillTransition.setFromValue(Color.RED);
   fillTransition.setToValue(Color.DARKGREEN);
   fillTransition.setDuration(duracao);
   fillTransition.setShape((Shape) alvo);
   t = fillTransition;
   break;
  case ROTATE:
   RotateTransition rotateTransition = new RotateTransition();
   rotateTransition.setByAngle(360);
   rotateTransition.setDuration(duracao);
   rotateTransition.setNode(alvo);
   t = rotateTransition;
   break;
  case SCALE:
   ScaleTransition scaleTransition = new ScaleTransition();
   scaleTransition.setFromX(1);
   scaleTransition.setFromY(1);
   scaleTransition.setToX(4);
   scaleTransition.setToY(4);
   scaleTransition.setDuration(duracao);
   scaleTransition.setNode(alvo);
   t = scaleTransition;
   break;
  case TRANSLATE:
   TranslateTransition translateTransition = new TranslateTransition();
   translateTransition.setToX(600);
   translateTransition.setToY(250);
   translateTransition.setDuration(duracao);
   translateTransition.setNode(alvo);
   t = translateTransition;
   break;
  }
  t.setAutoReverse(true);
  t.setCycleCount(2);
  return t;
 }
}


Isso obviamente não é "bonito", mas esse código não utiliza as melhores práticas de codificação, ele é somente para demonstrar as transições. As 5 transições aí criadas mostram como usamos os métodos mais básicos da transição. Primeiramente criamos temos um atributo que é do tipo Transition, ou seja, uma classe abstrata, então de acordo com o valor do enum criamos uma transição concreta e atribuimos à transição abstrata.
Note a repetição de uso dos métodos setNode e setDuration. Esses métodos estão em quase todas as transições, mas não em todos, por isso eles não puderam ser definidos na classe abstrata. já nas seguintes duas linhas em destaque, temos dois métodos que são da classe abstrata, logo não precisamos repetir a chamada do mesmo para cada transição criada.

  public static Transition fazerTransicao(Transicoes transicao,
  

   switch (transicao) {
   case FADE:
    // ...
    fadeTransition.setDuration(duracao);
    fadeTransition.setNode(alvo);
    t = fadeTransition;
    break;
   case FILL:
    //...
    fillTransition.setDuration(duracao);
    fillTransition.setShape(alvo);
    t = fillTransition;
    break;
   case ROTATE:
    //...
    rotateTransition.setDuration(duracao);
    rotateTransition.setNode(alvo);
    t = rotateTransition;
    break;
   case SCALE:
    //...
    scaleTransition.setDuration(duracao);
    scaleTransition.setNode(alvo);
    t = scaleTransition;
    break;
   case TRANSLATE:
    //...
    translateTransition.setDuration(duracao);
    translateTransition.setNode(alvo);
    t = translateTransition;
    break;
   }
   
   t.setAutoReverse(true);
   t.setCycleCount(2);
   return t;
  }

Esses dois métodos simplesmente irão fazer com que a transição mude o atributo do nó e volte para o valor original. Para isso precisamos ter dois ciclos (ciclos são quantas vezes a transição será tocada).

Os botões na parte acima da aplicação são do tipo "ToggleButton" e são parte de um grupo. Esse grupo é gerado baseado no seguinte Enum que você pode ver declarado na classe FabricaTransicao mostrado acima. O Enum contém as transições que a Fábrica suporta e de acordo com os valores dele, a gente gera os botões. Veja o código abaixo:

private HBox criaPainelSuperior() {
 HBox hbTopo = new HBox(10);
 hbTopo.setSpacing(10);
 hbTopo.setAlignment(Pos.CENTER);
 Transicoes[] transicoes = Transicoes.values();
 // grupo para todas as transições
 botoesTransicao = new ToggleGroup();
 for (int i = 0; i < transicoes.length; i++) {
  Transicoes t = transicoes[i];
  ToggleButton tb = new ToggleButton(t.name());
  tb.setUserData(t);
  if (i == 0) {
   tb.setSelected(true);
  }
  tb.setToggleGroup(botoesTransicao);
  hbTopo.getChildren().add(tb);
 }
 return hbTopo;
}

Perceba que através do método setUserData nós adicionamos a cada botão o valor do enum que esse botão representa e usamos esse valor para fabricar nossa transição.  
Quando você clica no botão "Tocar", a "action" do mesmo será usar a fábrica para produzir uma transição de acordo com o botão selecionado, configurar o comportamento dos botões para que os mesmos não fiquem ativos quando a transição estiver tocando(ou que fiquem ativos só quando a transição estiver tocando). Veja a ação do botão Tocar:

btnTocar.setOnAction(new EventHandler() {
 @Override
 public void handle(ActionEvent e) {
  // antes de tocar, pegamos a mais nova transição selecionada
  Transicoes t = (Transicoes) botoesTransicao.getSelectedToggle()
    .getUserData();
  transicaoAtual = FabricaTransicao.fazerTransicao(t,
    sldTempo.getValue());
  // lógicas de habilitação dos botões, temos que setar todas as
  // vezes pq trocamos as transições
  btnParar.disableProperty().bind(
    transicaoAtual.statusProperty().isNotEqualTo(
      Status.RUNNING));
  btnTocar.disableProperty().bind(
    transicaoAtual.statusProperty().isEqualTo(
      Status.RUNNING));
  btnPausar.disableProperty().bind(
    transicaoAtual.statusProperty().isNotEqualTo(
      Status.RUNNING));
  btnAjusta.disableProperty().bind(
    transicaoAtual.statusProperty().isEqualTo(
      Status.RUNNING));
  sldTempo.disableProperty().bind(
    transicaoAtual.statusProperty().isEqualTo(
      Status.RUNNING));
  System.out.println("Tocando transição " + t);
  transicaoAtual.play();
 }
});

Os outros botões irão controlar a transição (parar e pausar) e um serve para "ajustar" o texto quando a transição é parada no meio. Veja o método que é chamado quando clicamos nesse botão.

private void criaNoAlvo() {
 // configurar coisas do texto alvo...
 alvo = new Text("** Transições **");
 alvo.setFont(new Font(60));
 // efeitinsss
 Reflection efeito = new Reflection();
 efeito.setFraction(0.7);
 alvo.setEffect(efeito);
 alvo.setFill(Color.RED);
 raiz.setCenter(alvo);
}

O "slider" no canto direito será usado para que possamos selecionar o tempo total da transição.  Ao criar a transição, pegamos o valor dele e enviamos para o método de criação. Veja a criação e o uso do slider:

// criando o slider
sldTempo = new Slider(1, 10, 5);
//... criando a transição
transicaoAtual = FabricaTransicao.fazerTransicao(t, sldTempo.getValue(), alvo);

Conclusão

Nesse breve artigo nós apresentamos o código de uma aplicação que as transições do JavaFX. Talvez algumas coisas nesse artigo podem parecer um pouco obscuras, mas provavelmente é por que você não acompanha o blog há muito tempo! Sinta-se a vontade para explorar os artigos antigos que muitas coisas serão esclarecidas.
O código completo pode ser conferido no github. Comente esse artigo se tiver dúvidas ou use nosso grupo sobre JavaFX.




15 de set. de 2013

Criando animações usando as transições do JavaFX 2 - Teoria

Hoje vamos falar de algo mais interessante do que simples botões ou figuras geométricas: as transições! Com elas nós podemos criar animações simples com poquissímo código!

O que são transições?

Transições são um conjunto de classes da API do JavaFX 2 que  permitem que crie animações. Com ela nós podemos modificar o valor de uma propriedade em um dado tempo. Por exemplo, você poderia fazer um objeto mover da posição X 0 até a posição X 150 em 2 segundos e isso iria criar uma animação.
Após configurar os parâmetros corretamente, você pode decidir quando a transição começa e até parar antes da mesma terminar. Ou seja, você pode, por exemplo, utilizar um botão para disparar o início da transição e outro para parar. Para você ter ideia, abaixo seguem umas telas de aplicações que demonstram o uso de transições:



Transições disponíveis na API do JavaFX 2

As classes de transição oferecidades no JavaFX 2 ficam no pacote javafx.animation e as seguintes transições estão disponíveis para uso:

  • FadeTransition: Muda a opacidade(tranparência) de um nó. O FadeTransition permite variar essa opacidade em um dado tempo. Por exemplo, você pode configurar uma transição para em 1 segundo variar a opacidade de um nó(um botão, uma imagem, uma figura geométrica, etc) de 0 até 1 e isso fará com que o mesmo passe de invisível até ser completamente visível no tempo de 1 segundo;
  • FillTransition: Com a FillTransition nós podemos mudar a cor de preenchimento de um objetvo. Se um objeto tem o preenchimento com a cor azul e você usa a transição para mudar a cor de azul para rosa em um segundo, isso significa que a cor de preenchimento do mesmo irá iniciar totalmente azul e no espaço de tempo de 1 segundo irá se tornar completamente rosa, dando um efeito bastante interessante;
  • RotateTransition: Uma das propriedades mais comuns em um nó é a rotate (rotação). A RotateTransition mudará a rotação de um objeto de acordo com o tempo configurado. Por exemplo, você pode rotacionar um objeto de 0º até 90º em 2 segundos e após iniciar essa tranisção, ele irá ter a rotação modificada gradualmente até que o tempo termine;
  • ScaleTransition: A ScaleTransition server para você modificar a escala de um objeto, seja a altura ou a largura. Em outras palavras, você pode fazer a escala de um objeto ser modificada de X para X^2  em um dado intervalo de tempo;
  • TranslateTransition: Similarmente ao que foi falado na introdução desse artigo, a TranslateTransition irá modificar a posição X ou Y de um objeto em um dado intervalo de tempo;
  • StrokeTransition: A StrokeTransition é muito similar à FillTransition, no entanto, nessa transição a modificação não é do preenchimento, mas sim da linha que contorna o mesmo;
  • PathTransition: Embora complexa, a PathTransition é muito útil, pois permite que muitas ações sejam tomadas de acordo com o objeto Path passado;

A API do JavaFX também disponibiliza transições que permitem que várias transições sejam "tocadas" ao mesmo tempo, são elas:

SequentialTransition: A SequentialTransition server para adicionar diversas transições para serem "tocadas" de forma sequencial, ou seja, uma após a outra. Por exemplo, se adicionamos a transição t1, t2, t3 em uma SequentialTransition e tocar ela, t1 será ativada, em sequência será ativada t2 e por fim iremos fechar com t3; Se quisermos adicionar um tempo de espera entre as transições, podemos usar a PauseTransition, que é um tipo especial de transição cuja função é simplesmente "dar um tempo" antes da próxima transição da sequência;
ParallelTransition: A ParallelTransition atua de forma semelhante à SequentialTransition, mas toca todas as transições em paralelo, sendo que o tempo de duração será a da transição mais longa.



Lembre-se que todo componentes em uma aplicação JavaFX herda de (é um) nó e todos os nós tem propriedades em comum como rotação, posição X e Y, opacidade, etc;

Transições na prática

Como muitos já devem ter imaginado, temos uma classe comum para todas as transições, a classe Transition  Abaixo temos uma explicação de seus principais métodos:

play(): Começa a "tocar" a transição. Se a transição estava pausada antes, chamar esse método fará com que a transição volte a tocar do ponto onde tinha parada. Similar ao play, temos a playFromStart, que começa uma transição desde o começo e playFrom(Duration), onde podemos informar um ponto para a transição começar a tocar;
pause(): Para a transição em um dado momento, sendo que ao chamar play, a transição irá começar no momento parado por pause;
stop(): Para a transição por completo. A próxima vez que chamarmos play a transição irá iniciar do ponto inicial;
setOnFinished(EventHandler<ActionEvent>): Esse método é interessante! Com ele podemos informar uma ação que você deseja que seja feita assim que a transição termina de "tocar";
setAutoReverse(boolean value): Se chamarmos esse método e informar o valor true para value, iremos fazer com que a transição "volte", ou seja, se a transição for fazer um nó ir da posição X 0 para a posição 10, ao chamar setAutoReverse(true), o mesmo irá até a posição 10 e irá voltar para a posição 0 invés de parar na posição X 10;
setNode(Node): Aqui é onde falamos qual será nosso alvo
Outros métodos: Há outros métodos e atributos interessantes(acessados através de métodos) que você queria dar uma olhada. Para isso veja a documentação da classe que Transition veio, a Animation.

E o código?

Na próxima parte desse artigo vamos mostrar o código de uma aplicação que usa todas as transições do JavaFX. Eu sei que é "brochante" ler um textão desse e no fim não ter código, mas garanto um próximo artigo com bastante código pra gente discutir!

Conclusão

Transições é uma parte divertida da API do JavaFX. Com elas podemos criar animações com pouquíssimas linhas de código. Não perca o próximo artigo!