Esta página fue traducida automáticamente del portugués y puede contener errores. La versión en portugués es la canónica.
La pila de ventanas
Entender el modelo evita la mayoría de los bugs de navegación.
El modelo (LIFO)
El EventsProcessor mantiene una pila de Window. La ventana de la cima es la que recibe eventos y se
pinta por encima. Las de abajo siguen vivas (no se destruyen), solo cubiertas.
┌─────────────┐ ← topo: recebe toque/teclado, é pintada
│ TelaDetalhe │
├─────────────┤
│ TelaLista │ ← coberta, viva
├─────────────┤
│ TelaHome │ ← base
└─────────────┘
show()→ push: registra en elEventsProcessor, llama aonShow().hide()→ pop: desregistra, llama aonRemove(); la ventana de abajo vuelve a la cima.
Ciclo de vida por ventana
| Método | Cuándo | Uso |
|---|---|---|
onShow() |
al entrar en la pila | montar la UI (siempre super.onShow()) |
onReposition() |
al redimensionar/rotar | reorganizar/recalcular layout |
onRemove() |
al salir de la pila | liberar timers, cursores, listeners |
Fullscreen vs popup
La misma clase Window puede ser pantalla completa o popup — depende del WindowStyle
(widthRatio/heightRatio < 1 ⇒ popup; ver Diálogos y modales). Los popups
quedan sobre la pantalla anterior (que sigue visible detrás).
Botón Atrás (Android) y tocar fuera
- El botón físico Atrás es capturado por la aplicación y, por defecto, hace
hide()de la ventana de la cima. - En un popup con
canHide = true, tocar fuera cierra el popup. - Para impedir la salida (ej.: confirmación de descarte), maneja la intención antes de dejar que ocurra
hide().
Ejemplo completo (copiar, pegar y ejecutar)
Demuestra push/pop en la pila y una transición de entrada configurada en el WindowStyle de la pantalla apilada.
package com.exemplo;
import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;
/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
@Override
public void onShow() throws Exception
{
super.onShow();
setLayout(new VerticalLayout());
add(LAY_AUTO, new Label("Base da pilha (MainWindow)"));
Button empilhar = new Button("Empilhar tela (push)");
empilhar.onAction(c -> new TelaEmpilhada().show()); // push: entra al tope de la pila
add(LAY_AUTO, empilhar);
}
}
/** Pantalla que entra al tope con una transición y vuelve con hide() (pop). */
class TelaEmpilhada extends Window
{
@Override
public void onShow() throws Exception
{
super.onShow();
// transición de entrada/salida de esta ventana (la de abajo sigue viva detrás)
getStyle().transition(UIConstants.Transition.SLIDE_LEFT);
setLayout(new VerticalLayout());
add(LAY_AUTO, new Label("Topo da pilha — a tela de baixo continua viva"));
Button voltar = new Button("Voltar (pop)");
voltar.onAction(c -> hide()); // pop: sale de la pila, la de abajo vuelve al tope
add(LAY_AUTO, voltar);
}
}
Los fragmentos siguientes muestran solo el núcleo (lo que va dentro de
onShow()). Para ejecutar, colócalos en el esqueletoMainWindowanterior.
Transiciones
La animación de entrada/salida viene de WindowStyle.transition (UIConstants.Transition): NONE
(por defecto), FADE, SLIDE_LEFT/RIGHT/UP/DOWN, SCALE, SCALE_DOWN. Los pickers usan FADE por defecto.
getStyle().transition = UIConstants.Transition.SLIDE_LEFT; // desliza al abrir
Buenas prácticas
- No mantengas referencias a las ventanas de abajo más de lo necesario — deja que se recolecten al
hide(). - Todo
show()debe tener unhide()correspondiente (evita apilar pantallas sin retorno).
Errores comunes
- Reconstruir la UI en
onReposition()sin preservar el estado (scroll, selección). - Asumir que la ventana de abajo se destruyó al abrir otra — sigue viva.