JavaFX Switch scene with slide effect

2019-03-16 19:48发布

问题:

Is possible to switch scene with a slide effect?

i want that when i call the setScene on the stage instance it change the scene with a slide effect. it's possible?

public class ManyScenes extends Application {

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(final Stage primaryStage) {
        primaryStage.setTitle("Slide");

        Group root1 = new Group();
        Group root2 = new Group();

        Scene scene1 = new Scene(root1, 300, 250);
        Scene scene2 = new Scene(root2, 300, 250);

        Rectangle rectangle2 = new Rectangle(300, 250);
        rectangle2.setFill(Color.BLUE);
        root2.getChildren().add(rectangle2);

        Rectangle rectangle1 = new Rectangle(300, 250);
        rectangle1.setFill(Color.RED);
        root1.getChildren().add(rectangle1);



        primaryStage.setScene(scene1);
        primaryStage.show();

        // Here i  need a slide effect,
        // this method is called when a button is pressed.
        primaryStage.setScene(scene2);
    }
}

回答1:

You can't apply a transition between two scenes, as it is not possible to have both at the same time on one stage. One solution would be having just one scene and manage all the transitions inside, as in @James_D answer.

But you could also simulate a transition between two scenes. For this you can use two snapshots of both, perform a transition between those and at the end just set the new scene.

This is a very basic working case, where you can go backwards and forward all over again just clicking in the new scene:

@Override
public void start(Stage primaryStage) {
    Group root1 = new Group();
    Group root2 = new Group();

    Scene scene1 = new Scene(root1, 300, 250);
    Scene scene2 = new Scene(root2, 300, 250);

    Rectangle rectangle2 = new Rectangle(300, 250);
    rectangle2.setFill(Color.BLUE);
    root2.getChildren().add(rectangle2);

    Rectangle rectangle1 = new Rectangle(300, 250);
    rectangle1.setFill(Color.RED);
    root1.getChildren().add(rectangle1);

    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(scene1);
    primaryStage.show();

    rectangle1.setOnMouseClicked(e->{
        // Create snapshots with the last state of the scenes
        WritableImage wi = new WritableImage(300, 250);
        Image img1 = root1.snapshot(new SnapshotParameters(),wi);
        ImageView imgView1= new ImageView(img1);
        wi = new WritableImage(300, 250);
        Image img2 = root2.snapshot(new SnapshotParameters(),wi);
        ImageView imgView2= new ImageView(img2);
        // Create new pane with both images
        imgView1.setTranslateX(0);
        imgView2.setTranslateX(300);
        StackPane pane= new StackPane(imgView1,imgView2);
        pane.setPrefSize(300,250);
        // Replace root1 with new pane
        root1.getChildren().setAll(pane);
        // create transtition
        Timeline timeline = new Timeline();
        KeyValue kv = new KeyValue(imgView2.translateXProperty(), 0, Interpolator.EASE_BOTH);
        KeyFrame kf = new KeyFrame(Duration.seconds(1), kv);
        timeline.getKeyFrames().add(kf);
        timeline.setOnFinished(t->{
            // remove pane and restore scene 1
            root1.getChildren().setAll(rectangle1);
            // set scene 2
            primaryStage.setScene(scene2);
        });
        timeline.play();
    });
    rectangle2.setOnMouseClicked(e->{
        // Create snapshots with the last state of the scenes
        WritableImage wi = new WritableImage(300, 250);
        Image img1 = root1.snapshot(new SnapshotParameters(),wi);
        ImageView imgView1= new ImageView(img1);
        wi = new WritableImage(300, 250);
        Image img2 = root2.snapshot(new SnapshotParameters(),wi);
        ImageView imgView2= new ImageView(img2);
        // Create new pane with both images
        imgView2.setTranslateX(0);
        imgView1.setTranslateX(300);
        StackPane pane= new StackPane(imgView2,imgView1);
        pane.setPrefSize(300,250);
        // Replace root2 with new pane
        root2.getChildren().setAll(pane);
        // create transtition
        Timeline timeline = new Timeline();
        KeyValue kv = new KeyValue(imgView1.translateXProperty(), 0, Interpolator.EASE_BOTH);
        KeyFrame kf = new KeyFrame(Duration.seconds(1), kv);
        timeline.getKeyFrames().add(kf);
        timeline.setOnFinished(t->{
            // remove pane and restore scene 2
            root2.getChildren().setAll(rectangle2);
            // set scene 1
            primaryStage.setScene(scene1);
        });
        timeline.play();
    });

}

For more complex effects have a look at this.



回答2:

A Stage can contain one and only one Scene, and each Scene has one and only one root. So you need to manage the transition inside the root of a single scene.

Simple example:

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SlidingViews extends Application {

    @Override
    public void start(Stage primaryStage) {
        Rectangle rectangle1 = new Rectangle(300, 250);
        rectangle1.setFill(Color.RED);

        Button nextView = new Button("Next");
        nextView.setPadding(new Insets(10));
        BorderPane view1 = new BorderPane(rectangle1, null, null, nextView, null);
        BorderPane.setAlignment(nextView, Pos.CENTER);

        Group view2 = new Group();
        Rectangle rectangle2 = new Rectangle(300, 250);
        rectangle2.setFill(Color.BLUE);
        view2.getChildren().add(rectangle2);

        StackPane root = new StackPane(view1);

        nextView.setOnAction(event -> {
            root.getChildren().add(view2);
            double width = root.getWidth();
            KeyFrame start = new KeyFrame(Duration.ZERO,
                    new KeyValue(view2.translateXProperty(), width),
                    new KeyValue(view1.translateXProperty(), 0));
            KeyFrame end = new KeyFrame(Duration.seconds(1),
                    new KeyValue(view2.translateXProperty(), 0),
                    new KeyValue(view1.translateXProperty(), -width));
            Timeline slide = new Timeline(start, end);
            slide.setOnFinished(e -> root.getChildren().remove(view1));
            slide.play();
        });

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}


回答3:

AFAIK it's not possible. Rather than sliding scenes, try creating different layouts inside one scene and slide between them.



回答4:

I would recommend having a look at the Pagination control.

It switches its content panes with an animation. You can either customize this one to fit your needs or have a look at its skin implementation to get an idea on how to do the animation.