Quiero implementar algún tipo de sistema de notificación en mi aplicación pero tengo problemas con el cálculo de la posición real de mi notificación. Todas las notificaciones deben aparecer en una etapa separada y cada notificación debe estar alineada entre sí y cada notificación es un VBox simple con dos etiquetas (título y mensaje).
Creé una pequeña aplicación independiente con el problema que tengo.
Tan pronto como presione el botón en el escenario principal, se creará un VBox y se agregará a una segunda etapa de notificación. Tan pronto como sea necesario agregar una segunda notificación, esta segunda notificación debe estar debajo de la primera notificación y así sucesivamente. Por lo tanto, necesito encontrar la altura de la primera notificación para colocar la segunda notificación debajo.
Sé que podría usar un VBox en su lugar, pero en mi aplicación la notificación debería hacer una animación fluida y empujar las otras notificaciones más abajo. Eliminé toda la animación y eliminé parte de las notificaciones para que el ejemplo sea lo más pequeño posible.
El problema es que todos los cuadros de notificación tienen la misma altura, pero no la tienen (si modifica el texto y lo hace más largo o más pequeño).
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; } }Usé la herramienta ScenicView para verificar la altura y dice que la altura es 79, pero System.out me dice que la altura es 10.4. El valor 79 parece correcto, pero ¿cómo puedo obtener este valor en mi aplicación?
La respuesta corta es use applyCss() :
contentPane.applyCss(); contentPane.layout();De la documentación:
Si es necesario, aplique estilos a este Nodo y sus elementos secundarios, si los hay. Este método normalmente no necesita invocarse directamente, pero puede usarse junto con Parent.layout() para dimensionar un Nodo antes del siguiente pulso.
La respuesta larga y mejor es usar un VBox o un ListView .
Para agregar animación de diseño, use LayoutAnimator.java . Puede encontrar más detalles aquí .
Editar: un mre de usar LayoutAnimator para animar notificaciones recién agregadas:
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); } }