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.




Um comentário:

  1. Eu fiz assim, no meu caso foi mais util, mas seu post ajuda muito a entender como funciona.

    public void translate(int fromX, int fromY, int toX, int toY, double duration, Node node) {
    TranslateTransition translateTransitionAj = new TranslateTransition();
    translateTransitionAj.setFromX(fromX);
    translateTransitionAj.setToX(toX);
    translateTransitionAj.setFromY(fromY);
    translateTransitionAj.setToY(toY);
    translateTransitionAj.setDuration(Duration.millis(duration));
    translateTransitionAj.setNode(node);
    translateTransitionAj.play();
    }

    A chamada fica assim:

    translate(600, 0, 0, 0, 500, imgCadItem);

    translate(700, 0, 0, 0, 700, imgConsItem);
    translate(800, 0, 0, 0, 900, imgRemovItem);
    translate(900, 0, 0, 0, 1100, imgAltItem);

    translate(0, 200, 0, 0, 500, imgAjuda);
    translate(0, 200, 0, 0, 900, imgWebView);
    translate(0, 200, 0, 0, 1100, imgConfig);
    translate(0, 150, 0, 0, 700, labelAjuda);
    translate(0, 150, 0, 0, 1000, labelSugest);
    translate(0, 150, 0, 0, 1200, labelConf);
    pra cada componente chama-se o método novamente !

    Espero ter ajudado tanto quando você!

    Abraço

    ResponderExcluir