Creating Custom Animated Transitions with JavaFX

JavaFX comes with a number of animated transitions out of the box which are very easy to integrate into an application without too much effort. There are times though when the existing animations don’t cover a desired transition between UI states. Fortunately, implementing a custom animation is as easy as subclassing the javafx.animation.Transition class and providing an implementation of the interpolate() method. As an example, I had a JavaFX Parent component which I wanted to “grow” to a larger height if a large component was placed inside it. Rather than having the Parent component just jump to its new larger height, I wanted the transition to the new height to be animated over a specified amount of time. I was able to accomplish this effect by creating the ResizeHeightTranslation below.

import javafx.animation.Transition;
import javafx.scene.layout.Region;
import javafx.util.Duration;
public class ResizeHeightTranslation extends Transition {
protected Region region;
protected double startHeight;
protected double newHeight;
protected double heightDiff;
public ResizeHeightTranslation( Duration duration, Region region, double newHeight ) {
setCycleDuration(duration);
this.region = region;
this.newHeight = newHeight;
this.startHeight = region.getHeight();
this.heightDiff = newHeight startHeight;
}
@Override
protected void interpolate(double fraction) {
region.setMinHeight( startHeight + ( heightDiff * fraction ) );
}
}

In the constructor the duration of the animation is passed to the parent class, and the amount of space the component will need to grow is calculated.

public ResizeHeightTranslation( Duration duration, Region region, double newHeight ) {
setCycleDuration(duration);
this.region = region;
this.newHeight = newHeight;
this.startHeight = region.getHeight();
this.heightDiff = newHeight startHeight;
}

All that remains to do in the interpolate() method is to calculate the new height based on how far along the animation is. The interpolate() method will be called by the JavaFX runtime with values starting from 0.0 and going to 1.0 as the animation progresses.

@Override
protected void interpolate(double fraction) {
region.setMinHeight( startHeight + ( heightDiff * fraction ) );
}


Example Usage

In the example below I have a JavaFX Pane (salmon color), and a Button. When the Button is clicked I would like to add a TableView to the Pane. The TableView however is taller than the Pane is so I would like the Pane to increase in height so the table fits completely within the Pane. To give this state transition a polished effect, I would like the resizing of the Pane to be animated and then I would like the TableView to fade into view once the resizing is complete. Below is a screenshot of the UI, containing the Pane and the Button components.

screen-shot-2015-09-01-at-1-26-24-pm

The FXML for the UI is below

<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="346.0" prefWidth="498.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40" fx:controller="com.lynden.resizetest.FXMLController">
<children>
<Button fx:id="clickMeButton" layoutX="394.0" layoutY="294.0" onAction="#handleButtonAction" text="Click Me!" />
<Pane fx:id="contentPane" layoutX="25.0" layoutY="22.0" prefHeight="158.0" prefWidth="243.0" style="-fx-background-color: salmon;" />
</children>
</AnchorPane>

view raw

Scene.xml

hosted with ❤ by GitHub

In the controller class for the UI I have implemented and ActionListener method for the Button. When the Button is clicked a new TableView is created, along with a new ResizeHeightTranslation animation and a FadeTransition. Both of these animations are set to execute one right after the other by putting them in a SequentialTransition.

import com.lynden.glitter.animation.ResizeHeightTranslation;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.animation.FadeTransition;
import javafx.animation.SequentialTransition;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableView;
import javafx.scene.layout.Pane;
import javafx.util.Duration;
public class FXMLController implements Initializable {
@FXML
private Button clickMeButton;
@FXML
private Pane contentPane;
@FXML
private void handleButtonAction(ActionEvent event) {
TableView tableView = new TableView();
tableView.setMaxHeight(250);
tableView.setMinHeight(250);
ResizeHeightTranslation rht = new ResizeHeightTranslation(Duration.millis(1000), contentPane, tableView.getMinHeight() );
FadeTransition ft = new FadeTransition(Duration.millis(1000), tableView);
ft.setFromValue(0);
ft.setToValue(1);
SequentialTransition pt = new SequentialTransition(rht, ft);
pt.play();
contentPane.getChildren().add(tableView);
}
@Override
public void initialize(URL url, ResourceBundle rb) {
}
}

Finally, a short clip illustrating the animated transition in action.

twitter: @RobTerpilowski

2 thoughts on “Creating Custom Animated Transitions with JavaFX

  1. Pingback: Java desktop links of the week, September 7 « Jonathan Giles

  2. Pingback: JavaFX links of the week, September 7 // JavaFX News, Demos and Insight // FX Experience

Leave a Reply