动画添加组件到窗格

Animate adding components to a pane

我想在我的应用程序中实现某种通知系统,但我在计算通知的实际位置时遇到了问题。所有通知都应该出现在一个单独的阶段,每个通知应该相互对齐,每个通知都是一个带有两个标签(标题和消息)的简单 VBox。

我创建了一个小的独立应用程序来解决我遇到的问题。

只要在主舞台上按下按钮,就会创建一个 VBox 并将其添加到第二个通知舞台。一旦需要添加第二个通知,第二个通知应该在第一个通知下方,依此类推。因此我需要找到第一个通知的高度,以便将第二个通知放在下面。

我知道我可以改用 VBox,但在我的应用程序中,通知应该制作平滑的动画并将其他通知进一步向下推送。我删除了整个动画并删除了部分通知,因此示例尽可能小。

问题是所有通知框都具有相同的高度 - 但它们不是(如果您修改文本并使其更长/更小)。

package whatever;

import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class NotificationTest {

    private Stage notificationStage;

    private Pane contentPane;

    private static final Integer NOTIFICATION_WIDTH = 250;

    private Double notificationOffset = 0.0;

    private static final Integer SPACING_BETWEEN_NOTIFICATIONS = 20;

    public void start() {
        Stage mainStage = new Stage();
        TextField textField = new TextField("Some long text for testing purpose with even more letters in oder to create at least one linebreak...");
        Button button = new Button("Add Notification");
        button.setOnAction(actionEvent -> {
            addNotification(textField.getText());
        });

        VBox vBox = new VBox(10);
        vBox.getChildren().addAll(textField, button);
        mainStage.setScene(new Scene(vBox, 300, 300));
        mainStage.show();
    }

    private void addNotification(String text) {
        if(notificationStage == null) {
            notificationStage = new Stage();
            notificationStage.setWidth(NOTIFICATION_WIDTH);
            notificationStage.setHeight(Screen.getPrimary().getVisualBounds().getHeight() - 50);
            notificationStage.setX(Screen.getPrimary().getVisualBounds().getWidth() - 260);
            notificationStage.setY(50);
            contentPane = new Pane();
            contentPane.setStyle("-fx-background-color: transparent");
            notificationStage.setScene(new Scene(contentPane));
            notificationStage.initStyle(StageStyle.TRANSPARENT);
            notificationStage.getScene().setFill(Color.TRANSPARENT);
            notificationStage.show();
        }

        VBox notificationBox = new VBox(10);
        notificationBox.setMaxWidth(NOTIFICATION_WIDTH);
        notificationBox.setMinWidth(NOTIFICATION_WIDTH);
        notificationBox.setStyle("-fx-background-radius: 10; -fx-background-color: red");
        notificationBox.getChildren().add(new Label("Title of Notification"));
        Label message = new Label(text);
        message.setWrapText(true);
        notificationBox.getChildren().add(message);
        notificationBox.setLayoutY(notificationOffset);

        contentPane.getChildren().add(notificationBox);

        // Needs to be done - otherwise the height would be 0
        contentPane.layout();
        System.out.println(notificationBox.getHeight());

        notificationOffset += notificationBox.getHeight() + SPACING_BETWEEN_NOTIFICATIONS;
    }

}

我用 ScenicView 工具验证高度,它说高度是 79,但 System.out 告诉我高度是 10.4。 79 值似乎是正确的,但我如何在我的应用程序中获取此值?

简短的回答是使用 applyCss():

    contentPane.applyCss();
    contentPane.layout();

来自文档:

If required, apply styles to this Node and its children, if any. This method does not normally need to be invoked directly but may be used in conjunction with Parent.layout() to size a Node before the next pulse

长而更好的答案是使用 VBoxListView

要添加布局动画,请使用 LayoutAnimator.java. You can find more details here

编辑: 使用 LayoutAnimator 为新添加的通知设置动画:

import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;

public class NotificationTest extends Application {

    private Stage notificationStage;
    private Pane contentPane;
    private static final int NOTIFICATION_WIDTH = 250, SPACING_BETWEEN_NOTIFICATIONS = 20;
    private static final String LONG_TEXT = "Some long text for testing purpose with even more letters in oder to create "
                                            + "at least one linebreak...";
    private int counter = 0;

    @Override
    public void start(Stage mainStage) throws Exception {
        mainStage = new Stage();
        TextField textField = new TextField(LONG_TEXT);
        Button button = new Button("Add Notification");
        button.setOnAction(actionEvent -> {
            addNotification(textField.getText());
        });

        VBox vBox = new VBox(10, textField, button);
        mainStage.setScene(new Scene(vBox, 300, 300));
        mainStage.show();
    }

    private void addNotification(String text) {
        if(notificationStage == null) {
            notificationStage = new Stage();
            notificationStage.setWidth(NOTIFICATION_WIDTH);
            notificationStage.setX(Screen.getPrimary().getVisualBounds().getWidth() - 260);
            notificationStage.setY(50);
            contentPane = new VBox(SPACING_BETWEEN_NOTIFICATIONS);
            contentPane.setStyle("-fx-background-color: transparent");
            notificationStage.setScene(new Scene(contentPane));
            notificationStage.initStyle(StageStyle.TRANSPARENT);
            //animate using LayoutAnimator  https://gist.github.com/jewelsea/5683558
            LayoutAnimator ly = new LayoutAnimator();
            ly.observe(contentPane.getChildren());
            notificationStage.show();
        }

        VBox notificationBox = new VBox(10);
        notificationBox.setMaxWidth(NOTIFICATION_WIDTH);
        notificationBox.setMinWidth(NOTIFICATION_WIDTH);
        notificationBox.setStyle("-fx-border-color: black");
        notificationBox.getChildren().add(new Label("Title of Notification"));
        Label message = new Label(counter++ + ": " +text);
        message.setWrapText(true);
        notificationBox.getChildren().add(message);

        contentPane.getChildren().add(0, notificationBox);
    }

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