jfxtreetableview example java javafx observablelist treetableview

java - jfxtreetableview example



¿Puede ListChangeListener.Change.getRemoved() de JavaFX devolver elementos no contiguos? (1)

Tienes razón, el evento debe contener dos cambios ''eliminar'' separados. A partir del 1.8.0_74, el modelo de selección de TreeTableView parece irremediablemente roto. Ni siquiera es consistente con el modelo de selección de TreeView, que también es chiflado (pero no tanto). Hay tantos modos de falla, errores existentes y errores de regresión que es difícil saber si Oracle está al tanto del problema. Yo sugeriría la presentación de otro error. El código de abajo proporciona una caja de arena decente para jugar con la función

public class Test extends Application { public void start(Stage pStage) { pStage.setTitle("Test"); final TabPane tabPane = new TabPane(); tabPane.getTabs().addAll( Stream.of(true, false).map( pIsTreeTable -> { final Tab result = new Tab(pIsTreeTable ? "TreeTableView" : "TreeView"); // create tree model final TreeItem<String> root = new TreeItem<>("Root Node"); root.setExpanded(true); final Collection<TreeItem<String>> children = IntStream.rangeClosed( 1, 5 ).mapToObj(pIdx -> new TreeItem<>("Child Node " + pIdx)).collect( Collectors.toList() ); // create TreeView or TreeTableView final Control tree; final MultipleSelectionModel<TreeItem<String>> selectionModel; if (pIsTreeTable) { final TreeTableView<String> treeTableView = new TreeTableView<>( root ); final TreeTableColumn<String,String> column = new TreeTableColumn<>( "Column" ); column.setCellValueFactory( pTreeItem -> new ReadOnlyStringWrapper( pTreeItem.getValue().getValue() ) ); treeTableView.getColumns().add(column); tree = treeTableView; selectionModel = treeTableView.getSelectionModel(); } else { final TreeView<String> treeView = new TreeView<>(root); tree = treeView; selectionModel = treeView.getSelectionModel(); } selectionModel.setSelectionMode(SelectionMode.MULTIPLE); // add buttons final ToggleButton childrenBtn = new ToggleButton("Children"); childrenBtn.selectedProperty().addListener( (pObservable, pOldVal, pNewVal) -> { if (pNewVal) { root.getChildren().addAll(children); } else { root.getChildren().clear(); } } ); childrenBtn.setSelected(true); final ToggleButton showRootBtn = new ToggleButton("Show Root"); showRootBtn.setSelected(true); ( pIsTreeTable ? ((TreeTableView<?>) tree).showRootProperty() : ((TreeView<?>) tree).showRootProperty() ).bind(showRootBtn.selectedProperty()); // ''getSelectedItems()'' tab final Tab selectedItemsTab = new Tab("getSelectedItems()"); final TextArea selectedItemsTextArea = new TextArea(); selectionModel.getSelectedItems().addListener( (ListChangeListener<TreeItem<String>>) pChange -> { while (pChange.next()) { if (pChange.getRemovedSize() > 0) { selectedItemsTextArea.appendText( "Removed " + pChange.getRemoved() + ''/n'' ); } if (pChange.getAddedSize() > 0) { selectedItemsTextArea.appendText( "Added " + pChange.getAddedSubList() + ''/n'' ); } } selectedItemsTextArea.appendText( "Selection: " + pChange.getList() + "/n/n" ); } ); selectedItemsTab.setContent(selectedItemsTextArea); // ''getSelectedItem()'' tab final Tab selectedItemTab = new Tab("getSelectedItem()"); final TextArea selectedItemTextArea = new TextArea(); selectionModel.selectedItemProperty().addListener( (pObservable, pOldVal, pNewVal) -> { selectedItemTextArea.appendText("Selected " + pNewVal + ''/n''); } ); selectedItemTab.setContent(selectedItemTextArea); // display selection data in text area final TabPane selectionTabPane = new TabPane(); selectionTabPane.getTabs().addAll(selectedItemsTab, selectedItemTab); final SplitPane splitPane = new SplitPane( tree, new HBox(showRootBtn, childrenBtn), selectionTabPane ); splitPane.setOrientation(Orientation.VERTICAL); result.setContent(splitPane); return result; } ).collect(Collectors.toList()) ); pStage.setScene(new Scene(tabPane, 300, 450)); pStage.show(); } public static void main(String[] pArgs) {launch(pArgs);} }

Asuntos relacionados:

  1. Presione el botón ''Ctrl''
  2. Seleccione "Child Node 2"
  3. Seleccione "Child Node 3"
  4. Seleccione "Nodo Infantil 1"

El evento ListChangeListener.Change implica que se seleccionó "Nodo secundario 2".

  1. Seleccione "Nodo raíz"
  2. Deselecciona el botón "Mostrar raíz"

Se seleccionó "Nodo secundario 1" pero no se transmitió ningún evento de selección.

  1. Seleccione "Child Node 2"
  2. Deselecciona el botón "Niños"

La lista de ''selección'' incluye un nulo.

  1. Seleccione "Nodo Infantil 1"
  2. Contraer "nodo raíz"

El evento ListChangeListener.Change no incluye eliminaciones.

  1. Deselecciona el botón "Niños"
  2. Seleccione "Nodo raíz"
  3. Deselecciona el botón "Mostrar raíz"

El evento no se transmite ni para TreeView ni para TreeTableView. A partir de ahí, si se presiona el botón "Mostrar raíz", TreeView transmite el evento pero TreeTableView no lo hace.

Problema

Cuando los elementos se eliminan de una lista ObservableList , se getFrom() un evento de cambio donde getFrom() proporciona la ubicación de la eliminación y getRemoved() proporciona una lista de los elementos que se eliminaron. La documentación dice:

El método getRemoved() devuelve una lista de elementos que han sido reemplazados o eliminados de la lista.

No se indica como tal, pero entendí que la lista de elementos es una sub-lista contigua de la lista original. He escrito mucho código con esa suposición, pero ahora estoy encontrando dificultades con el modelo de selección de TreeTableView , que no se comporta de esa manera.

Ejemplo

Tomemos, por ejemplo, una tabla de árbol simple con tres filas "Nodo". Si selecciono esas tres filas ...

... y luego haga clic y seleccione solo la fila del medio ...

... el evento de cambio treeTableView.getSelectionModel().getSelectedItems() en treeTableView.getSelectionModel().getSelectedItems() tiene este aspecto:

{ [TreeItem [ value: Node 1 ], TreeItem [ value: Node 3 ]] removed at 0, }

En un evento de cambio único, informa que "Nodo 1" y "Nodo 3" se eliminaron del índice 0 de la lista de artículos selectedItems .

Habría esperado que el objeto Change tuviera dos eventos de eliminación separados separados por las next() llamadas. La primera llamada a next() me diría que "Nodo 1" se eliminó en el índice 0, y la segunda llamada a next() me diría que "Node 3" se eliminó en el índice 1. Pero no, recibo una sola evento con ambas filas enumeradas a la vez.

Pregunta

¿Puede getRemoved() realmente devolver elementos no contiguos? ¿Es esto un malentendido de mi parte sobre cómo funcionan los eventos de cambio de lista, o es un error en TreeTableView ?

Normalmente dudo en culpar a las bibliotecas estándar, pero este no sería el primer error que encontré en JavaFX, por lo que no es impensable.

Actualizar

Si agrego una llamada a setShowRoot(false) , el comportamiento cambia. Obtuve lo que esperaba, la eliminación se dividió en dos partes:

{ [TreeItem [ value: Node 1 ]] removed at 0, [TreeItem [ value: Node 3 ]] removed at 1, }

Además, aquí está mi MCVE :

import java.util.*; import javafx.application.*; import javafx.beans.property.*; import javafx.collections.*; import javafx.scene.*; import javafx.scene.control.*; import javafx.stage.*; public class TreeTableSelectionEvents extends Application { public void start(Stage stage) { // Root node. TreeItem<String> root = new TreeItem<>("Root"); root.setExpanded(true); root.getChildren().setAll(Arrays.asList( new TreeItem<>("Node 1"), new TreeItem<>("Node 2"), new TreeItem<>("Node 3") )); // Single column. TreeTableColumn<String, String> column = new TreeTableColumn<>("Column"); column.setPrefWidth(150); column.setCellValueFactory((TreeTableColumn.CellDataFeatures<String, String> p) -> { return new ReadOnlyStringWrapper(p.getValue().getValue()); }); // Tree table. TreeTableView<String> table = new TreeTableView<>(root); table.getColumns().add(column); table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); // table.setShowRoot(false); table.getSelectionModel().getSelectedItems().addListener( (ListChangeListener.Change<? extends TreeItem<String>> change) -> { System.out.printf("item change = %s, list is now %s%n", change, change.getList()); } ); table.getSelectionModel().getSelectedIndices().addListener( (ListChangeListener.Change<? extends Integer> change) -> { System.out.printf("index change = %s, list is now %s%n", change, change.getList()); } ); // Stage. stage.setScene(new Scene(table)); stage.show(); } }