setonaction event event-handling textarea javafx-2

event-handling - setonaction - javafx event handler



JavaFX 2 TextArea: cómo evitar que consuma (1)

La solución aquí muestra dos áreas de texto y un botón predeterminado. Cuando el usuario presiona la tecla de tabulación, el foco se mueve al siguiente control hacia abajo. Cuando el usuario presiona la tecla Intro, se dispara el botón predeterminado.

Para lograr este comportamiento:

  1. La tecla Intro presionar para cada área de texto se captura en un filtro de eventos, se copia y se dirige al nodo principal del área de texto (que contiene el botón Aceptar predeterminado). Esto hace que se active el botón OK predeterminado cuando se presiona intro en cualquier parte del formulario. La tecla Intro original se consume para que no se agregue una nueva línea al texto del área de texto.
  2. La pulsación de la tecla de tabulación para cada área de texto se captura en un filtro y la lista de direccionabilidad de enfoque del padre se procesa para encontrar el siguiente control enfocable y se solicita el enfoque para ese control. La pulsación de tecla de tabulación original se consume para que no se agregue espacio de tabulación al texto del área de texto.

El código hace uso de características implementadas en Java 8, por lo que se requiere Java 8 para ejecutarlo.

import javafx.application.Application; import static javafx.application.Application.launch; import javafx.beans.value.*; import javafx.collections.ObservableList; import javafx.event.*; import javafx.scene.*; import javafx.scene.control.*; import static javafx.scene.input.KeyCode.ENTER; import static javafx.scene.input.KeyCode.TAB; import javafx.scene.input.KeyEvent; import javafx.scene.layout.VBox; import javafx.stage.*; public class TextAreaTabAndEnterHandler extends Application { final Label status = new Label(); public static void main(String[] args) { launch(args); } @Override public void start(final Stage stage) { final TextArea textArea1 = new TabAndEnterIgnoringTextArea(); final TextArea textArea2 = new TabAndEnterIgnoringTextArea(); final Button defaultButton = new Button("OK"); defaultButton.setDefaultButton(true); defaultButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { status.setText("Default Button Pressed"); } }); textArea1.textProperty().addListener(new ClearStatusListener()); textArea2.textProperty().addListener(new ClearStatusListener()); VBox layout = new VBox(10); layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10px;"); layout.getChildren().setAll( textArea1, textArea2, defaultButton, status ); stage.setScene( new Scene(layout) ); stage.show(); } class ClearStatusListener implements ChangeListener<String> { @Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) { status.setText(""); } } class TabAndEnterIgnoringTextArea extends TextArea { final TextArea myTextArea = this; TabAndEnterIgnoringTextArea() { addEventFilter(KeyEvent.KEY_PRESSED, new TabAndEnterHandler()); } class TabAndEnterHandler implements EventHandler<KeyEvent> { private KeyEvent recodedEvent; @Override public void handle(KeyEvent event) { if (recodedEvent != null) { recodedEvent = null; return; } Parent parent = myTextArea.getParent(); if (parent != null) { switch (event.getCode()) { case ENTER: if (event.isControlDown()) { recodedEvent = recodeWithoutControlDown(event); myTextArea.fireEvent(recodedEvent); } else { Event parentEvent = event.copyFor(parent, parent); myTextArea.getParent().fireEvent(parentEvent); } event.consume(); break; case TAB: if (event.isControlDown()) { recodedEvent = recodeWithoutControlDown(event); myTextArea.fireEvent(recodedEvent); } else { ObservableList<Node> children = parent.getChildrenUnmodifiable(); int idx = children.indexOf(myTextArea); if (idx >= 0) { for (int i = idx + 1; i < children.size(); i++) { if (children.get(i).isFocusTraversable()) { children.get(i).requestFocus(); break; } } for (int i = 0; i < idx; i++) { if (children.get(i).isFocusTraversable()) { children.get(i).requestFocus(); break; } } } } event.consume(); break; } } } private KeyEvent recodeWithoutControlDown(KeyEvent event) { return new KeyEvent( event.getEventType(), event.getCharacter(), event.getText(), event.getCode(), event.isShiftDown(), false, event.isAltDown(), event.isMetaDown() ); } } } }

Una solución alternativa sería implementar su propia máscara personalizada para TextArea, que incluye un nuevo comportamiento de manejo de claves. Creo que tal proceso sería más complicado que la solución presentada aquí.

Actualizar

Una cosa que realmente no me gustó de mi solución original a este problema fue que una vez que se consumió la tecla Tab o Enter, no había manera de desencadenar su procesamiento predeterminado. Así que actualicé la solución de manera que si el usuario mantiene presionada la tecla de control al presionar Tab o Enter, se realizará la operación predeterminada de tabulación o entrada. Esta lógica actualizada permite al usuario insertar una nueva línea o espacio de tabulación en el área de texto presionando CTRL + Enter o CTRL + Tab.

Quiero usar JavaFX TextArea como si fuera exactamente como un TextField multilínea. En otras palabras, cuando presiono [Tab] quiero pasar al siguiente control en el formulario y cuando presiono [Enter] quiero que Key.Event vaya al control DefaultButton (en lugar de ser consumido por TextArea).

El comportamiento predeterminado para TextArea es que [Tab] se inserta en TextArea y [Enter] inserta un carácter de nueva línea.

Sé que necesito usar EventFilters para obtener el comportamiento que quiero, pero me equivoco. No quiero que TextArea consuma estos eventos ... Solo quiero que les permita "seguir adelante".