In my program, I want objects to appear that they are moving from one spot to another. I used the TranslateTransition
. For it to work, I need to give it a starting location, in my case, the distance to the starting node.
The problem, is the two nodes are in separate containers, so simply using layoutX
and layoutY
in the calculation will not work.
In my code below, I used the localToScene
method for converting coordinates relative to the local node to the scene. Then I did a little subtraction to get the distance between the two nodes relative to the moving node.
import com.neonorb.commons.log.Log;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Node;
import javafx.scene.Parent;
import sun.reflect.generics.reflectiveObjects.LazyReflectiveObjectGenerator;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
/**
* Created by chris on 8/23/15.
*/
public class TranslateUtils {
public static DoubleProperty getTranslateX(Node translatingNode, Node otherNode) {
DoubleProperty ret = new SimpleDoubleProperty();
Platform.runLater(() -> {
List<Observable> dependencies = getAllParents(translatingNode).stream().map(Node::layoutXProperty).collect(Collectors.toList());
dependencies.addAll(getAllParents(otherNode).stream().map(Node::layoutXProperty).collect(Collectors.toList()));
ret.bind(Bindings.createDoubleBinding(new Callable<Double>() {
@Override
public Double call() throws Exception {
Log.debug("recalculating x");
return otherNode.localToScene(otherNode.getBoundsInLocal()).getMinX() - translatingNode.localToScene(translatingNode.getBoundsInLocal()).getMinX();
}
}, dependencies.toArray(new Observable[dependencies.size()])));
});
return ret;
}
public static DoubleProperty getTranslateY(Node translatingNode, Node otherNode) {
DoubleProperty ret = new SimpleDoubleProperty();
Platform.runLater(() -> {
List<Observable> dependencies = getAllParents(translatingNode).stream().map(Node::layoutXProperty).collect(Collectors.toList());
dependencies.addAll(getAllParents(otherNode).stream().map(Node::layoutXProperty).collect(Collectors.toList()));
ret.bind(Bindings.createDoubleBinding(new Callable<Double>() {
@Override
public Double call() throws Exception {
Log.debug("recalculating y");
return otherNode.localToScene(otherNode.getBoundsInLocal()).getMinY() - translatingNode.localToScene(translatingNode.getBoundsInLocal()).getMinY();
}
}, dependencies.toArray(new Observable[dependencies.size()])));
});
return ret;
}
private static List<Parent> getAllParents(Node node) {
List<Parent> parents = new ArrayList<Parent>();
Parent parent = node.getParent();
if (parent != null) {
parents.addAll(getAllParents(parent));
parents.add(parent);
}
return parents;
}
}
You can test the code with this:
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;
import org.controlsfx.tools.Borders;
import java.io.IOException;
/**
* Created by chris on 7/20/15.
*/
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws IOException {
BorderPane borderPane = new BorderPane();
Label translatingNode = new Label("translating node");
HBox translatingHBox = new HBox(translatingNode);
translatingHBox.setAlignment(Pos.CENTER);
borderPane.setTop(translatingHBox);
Label referenceNode = new Label("reference node");
HBox referenceHBox = new HBox(referenceNode);
referenceHBox.setAlignment(Pos.CENTER_RIGHT);
borderPane.setBottom(referenceHBox);
TranslateTransition translateTransition = new TranslateTransition(Duration.seconds(3), translatingNode);
translateTransition.fromXProperty().bind(TranslateUtils.getTranslateX(translatingNode, referenceNode));
translateTransition.fromYProperty().bind(TranslateUtils.getTranslateY(translatingNode, referenceNode));
translateTransition.setToX(0);
translateTransition.setToY(0);
Button playButton = new Button("play");
playButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
translateTransition.play();
}
});
borderPane.setCenter(playButton);
primaryStage.setScene(new Scene(borderPane));
primaryStage.show();
}
}
You can successfully play the animation the first time, but try resizing the window. The translate locations don't get updated.
I ended up using custom methods to find the global location:
private static double getGlobalX(Node node) {
if (node == null) {
return 0.0;
}
double parentGlobalX = getGlobalX(node.getParent());
return node.getLayoutX() - parentGlobalX;
}
private static double getGlobalY(Node node) {
if (node == null) {
return 0.0;
}
double parentGlobalY = getGlobalY(node.getParent());
return parentGlobalY - node.getLayoutY();
}