Skip to content

Instantly share code, notes, and snippets.

@teyc
Last active May 24, 2020 09:52
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save teyc/5668517 to your computer and use it in GitHub Desktop.
Save teyc/5668517 to your computer and use it in GitHub Desktop.
JavaFX helper which slides elements inside a layout into its final position
package javafxapplication1;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.animation.Transition;
import javafx.animation.TranslateTransition;
import javafx.beans.property.DoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.util.Duration;
/**
* Animates an object when its position is changed. For instance, when
* additional items are added to a Region, and the layout has changed, then the
* layout animator makes the transition by sliding each item into its final
* place.
*/
public class LayoutAnimator implements ChangeListener, ListChangeListener<Node> {
private Map<Node, Transition> nodesInTransition;
public LayoutAnimator() {
this.nodesInTransition = new HashMap<>();
}
/**
* Animates all the children of a Region.
* <code>
* VBox myVbox = new VBox();
* LayoutAnimator animator = new LayoutAnimator();
* animator.observe(myVbox.getChildren());
* </code>
*
* @param nodes
*/
public void observe(ObservableList<Node> nodes) {
for (Node node : nodes) {
this.observe(node);
}
nodes.addListener(this);
}
public void unobserve(ObservableList<Node> nodes) {
nodes.removeListener(this);
}
public void observe(Node n) {
n.layoutXProperty().addListener(this);
n.layoutYProperty().addListener(this);
}
public void unobserve(Node n) {
n.layoutXProperty().removeListener(this);
n.layoutYProperty().removeListener(this);
}
@Override
public void changed(ObservableValue ov, Object oldValue, Object newValue) {
final Double oldValueDouble = (Double) oldValue;
final Double newValueDouble = (Double) newValue;
final Double changeValueDouble = newValueDouble - oldValueDouble;
DoubleProperty doubleProperty = (DoubleProperty) ov;
Node node = (Node) doubleProperty.getBean();
final TranslateTransition t;
if ((TranslateTransition) nodesInTransition.get(node) == null) {
t = new TranslateTransition(Duration.millis(150), node);
} else {
t = (TranslateTransition) nodesInTransition.get(node);
}
if (doubleProperty.getName().equals("layoutX")) {
Double orig = node.getTranslateX();
if (Double.compare(t.getFromX(), Double.NaN) == 0) {
t.setFromX(orig - changeValueDouble);
t.setToX(orig);
}
}
if (doubleProperty.getName().equals("layoutY")) {
Double orig = node.getTranslateY();
if (Double.compare(t.getFromY(), Double.NaN) == 0) {
t.setFromY(orig - changeValueDouble);
t.setToY(orig);
}
}
t.play();
}
@Override
public void onChanged(ListChangeListener.Change change) {
while (change.next()) {
if (change.wasAdded()) {
for (Node node : (List<Node>) change.getAddedSubList()) {
this.observe(node);
}
} else if (change.wasRemoved()) {
for (Node node : (List<Node>) change.getRemoved()) {
this.unobserve(node);
}
}
}
}
}
package javafxapplication1;
import java.util.Random;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
/**
* Creates a FlowPane and adds some rectangles inside.
* A LayoutAnimator is set to observe the contents of the FlowPane for layout
* changes.
*/
public class TestLayoutAnimate extends Application {
@Override
public void start(Stage primaryStage) {
final Pane root = new FlowPane();
// Clicking on button adds more rectangles
Button btn = new Button();
btn.setText("Add Rectangles");
final TestLayoutAnimate self = this;
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
self.addRectangle(root);
}
});
root.getChildren().add(btn);
// add 5 rectangles to start with
for (int i = 0; i < 5; i++) {
addRectangle(root);
}
root.layout();
LayoutAnimator ly = new LayoutAnimator();
ly.observe(root.getChildren());
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Flow Layout Test");
primaryStage.setScene(scene);
primaryStage.show();
}
protected void addRectangle(Pane root) {
Random rnd = new Random();
Rectangle nodeNew = new Rectangle(50 + rnd.nextInt(20), 40 + rnd.nextInt(20));
nodeNew.setStyle("-fx-margin: 10;");
String rndColor = String.format("%02X", rnd.nextInt(), rnd.nextInt(), rnd.nextInt());
try {
Paint rndPaint = Paint.valueOf(rndColor);
nodeNew.setFill(rndPaint);
} catch (Exception e) {
nodeNew.setFill(Paint.valueOf("#336699"));
}
nodeNew.setStroke(Paint.valueOf("black"));
root.getChildren().add(0, nodeNew);
}
}
@mej71
Copy link

mej71 commented Jan 8, 2019

Would it be alright if I use this in an open source project with credit? I don't see a license posted
Great work btw

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment