10.2 Gráfico de escena
1. Descripción general¶
Un gráfico de escena es una estructura de datos de árbol que organiza (y agrupa) objetos gráficos para una representación lógica más sencilla. También permite que el motor de gráficos represente los objetos de la manera más eficiente al omitir total o parcialmente los objetos que no se verán en la imagen final. La siguiente figura muestra un ejemplo de la arquitectura del gráfico de escena JavaFX.
En la parte superior de la arquitectura hay un Stage. Una etapa es una representación JavaFX de una ventana de sistema operativo nativo. En un momento dado, un escenario puede tener un solo Scene adjunto. Una escena es un contenedor para el gráfico de escena JavaFX.
Todos los elementos en el gráfico de escena JavaFX (como pueden ser botones, etiquetas, paneles, etc) se representan como Node objetos. Hay tres tipos de nodos: raíz, rama y hoja. El nodo raíz es el único nodo que no tiene un padre y está contenido directamente en una escena, que se puede ver en la figura anterior. La diferencia entre una rama y una hoja es que un nodo hoja no tiene hijos.
En el gráfico de escena, los nodos secundarios comparten muchas propiedades de un nodo principal. Por ejemplo, una transformación o un evento aplicado a un nodo padre también se aplicará recursivamente a sus hijos. Como tal, una jerarquía compleja de nodos se puede ver como un solo nodo para simplificar el modelo de programación. Exploraremos transformaciones y eventos en secciones posteriores.
En la siguiente figura se puede ver un ejemplo de un gráfico de escena "Hola Mundo".
Una posible implementación que producirá un gráfico de escena que coincida con la figura anterior es el siguiente ejemplo:
Ejemplo Hola Mundo: E01_HolaMundo.java
Vamos comenzar por el archifamoso programa de inicialización en el mundo de la programación:
- Heredando la clase principal de la clase
Application. - Tendremos que importar las clases de la librería.
- Implementar el método abstracto, añadir el
launch()almain()y añadir los imports necesarios.
- Un nodo puede tener un máximo de 1 padre.
- Un nodo en el gráfico de escena "activo" (adjunto a una escena actualmente visible) solo se puede modificar desde el subproceso de la aplicación JavaFX.
- La clase
HolaMundoextiendeApplication. Application es la clase base para cualquier aplicación JavaFX y proporciona el métodostartque debe ser sobrescrito. - El método
createContent()devuelve un objetoParent, que es un nodo raíz en la escena. Aquí, se usaStackPane, que es un contenedor que organiza los elementos apilándolos. Dentro de StackPane, se coloca unText("Hola Mundo"), que es el nodo de texto que se verá en la ventana. start(Stage stage)es el punto de entrada de la aplicación JavaFX. Se ejecuta cuando la aplicación se inicia:stage.setTitle("Hola Mundo");establece el título de la ventana.stage.setScene(new Scene(createContent(), 400, 400));se crea una escena (Scene) con el contenido generado encreateContent(). La escena tiene un tamaño de 400x400 píxeles.stage.show();muestra la ventana en la pantalla.
launch(args);inicia la aplicación JavaFX. Este método llama internamente astart(Stage stage)después de inicializar el entorno de JavaFX.
Si hemos seguido todos los pasos correctamente, podremos ejecutar nuestra aplicación y ver la ventana titulada "Hola Mundo":
2. Transformaciones¶
Usaremos la siguiente aplicación como ejemplo para demostrar las 3 transformaciones más comunes: E02_TransformApp.java
Ejemplo E02_TransformApp.java
Ejecutar la aplicación dará como resultado la siguiente imagen:
En JavaFX, puede ocurrir una transformación simple en uno de los 3 ejes: X, Y o Z. La aplicación de ejemplo está en 2D, por lo que solo consideraremos los ejes X e Y.
2.1. Translación¶
En JavaFX y gráficos por computadora, translate significa moverse.
Un ejemplo de esto es que podemos trasladar nuestra caja en 100 píxeles en el eje X y 200 píxeles en el eje Y:
2.2. Escala¶
Puede aplicar la escala para hacer un nodo más grande o más pequeño. El valor de escala es una relación. Por defecto, un nodo tiene un valor de escala de 1 (100%) en cada eje.
Un ejemplo de esto es que podemos agrandar nuestra caja aplicando una escala de 1.5 en los ejes X e Y:
2.3. Rotación¶
La rotación de un nodo determina el ángulo en el que se representa el nodo. En 2D el único eje de rotación sensible es el eje Z.
Un ejemplo de esto es que podemos girar la caja 30 grados:
3. Manejo de eventos¶
Un evento notifica que ha ocurrido algo importante. Los eventos suelen ser lo "primitivo" de un sistema de eventos (también conocido como bus de eventos). Generalmente, un sistema de eventos tiene las siguientes 3 responsabilidades:
fire(desencadenar) un evento,- notificar
listeners(a las partes interesadas) sobre el evento y handle(procesar) el evento.
El mecanismo de notificación de eventos lo realiza la plataforma JavaFX automáticamente. Por lo tanto, solo consideraremos cómo disparar eventos, escuchar eventos y cómo manejarlos.
Primero, vamos a crear un evento personalizado:
Ejemplo E03_EventoUsuario.java
Dado que los tipos de eventos son fijos, generalmente se crean dentro del mismo archivo de origen que el evento. Podemos ver que hay 2 tipos específicos de eventos: LOGIN_SUCCEEDEDy LOGIN_FAILED. Podemos escuchar estos tipos específicos de eventos:
Alternativamente, podemos manejar cualquier UserEvent:
Finalmente, podemos construir y disparar nuestros propios eventos:
Por ejemplo, LOGIN_SUCCEEDEDo LOGIN_FAILED podría activarse cuando un usuario intenta iniciar sesión en una aplicación. Según el resultado del inicio de sesión, podemos permitir que el usuario acceda a la aplicación o bloquearlo. Si bien se puede lograr la misma funcionalidad con una if declaración simple, hay una ventaja significativa de un sistema de eventos. Los sistemas de eventos se diseñaron para permitir la comunicación entre varios módulos (subsistemas) en una aplicación sin acoplarlos estrechamente. Como tal, un sistema de audio puede reproducir un sonido cuando el usuario inicia sesión. Por lo tanto, mantiene todo el código relacionado con el audio en su propio módulo. Sin embargo, no profundizaremos en los estilos arquitectónicos.
3.1. Eventos de entrada¶
Los eventos de teclado y ratón son los tipos de eventos más comunes utilizados en JavaFX. Cada Node proporciona los llamados "métodos de conveniencia" para manejar estos eventos. Por ejemplo, podemos ejecutar algún código cuando se presiona un botón:
Para mayor flexibilidad también podemos usar lo siguiente:
El objeto e anterior es de tipo MouseEvent y se puede consultar para obtener información diversa sobre el evento, por ejemplo, x posiciones y, número de clics, etc. Finalmente, podemos hacer lo mismo con las teclas:
El objeto eaquí es de tipo KeyEvent y lleva información sobre el código de la tecla, que luego se puede asignar a una tecla física real en el teclado.
4. Sincronización¶
Es importante comprender la diferencia de tiempo entre la creación de controles de interfaz de usuario de JavaFX y la visualización de los controles. Al crear los controles de la interfaz de usuario, ya sea a través de la creación directa de objetos API o mediante FXML, es posible que te falten ciertos valores de geometría de pantalla, como las dimensiones de una ventana. Eso está disponible más tarde, en el instante en que se muestra la pantalla al usuario. Ese evento de visualización, llamado OnShown, es el momento en que se ha asignado una ventana y se completan los cálculos de diseño final.
Para demostrar esto, considere el siguiente programa que muestra las dimensiones de la pantalla mientras se crean los controles de la interfaz de usuario y las dimensiones de la pantalla cuando se muestra la pantalla. La siguiente captura de pantalla muestra la ejecución del programa. Cuando se crean los controles de la interfaz de usuario (new VBox(), new Scene(), primaryStage.setScene()), no hay valores reales de alto y ancho de ventana disponibles como lo demuestran los valores "NaN" indefinidos.
Sin embargo, los valores de ancho y alto están disponibles una vez que se muestra la ventana. El programa registra un controlador de eventos para el evento OnShown y prepara la misma salida.
La siguiente es la clase Java del programa de demostración:
Ejemplo E04_StartVsShown.java
A veces, se conocerá las dimensiones de la pantalla de antemano y se puede usar esos valores en cualquier punto del programa JavaFX. Esto incluye antes del evento OnShown.
Sin embargo, si su secuencia de inicialización contiene lógica que necesita estos valores, deberá trabajar con el evento OnShown. Un caso de uso podría ser trabajar con las últimas dimensiones guardadas o dimensiones basadas en la entrada del programa.