Home > Mobile >  Is there a TreeView.getRow() option that does not take into account the current state of each tree i
Is there a TreeView.getRow() option that does not take into account the current state of each tree i

Time:06-28

Thank you for taking the time to read my question.

Simply put I would like to be able to get the index of the selected TreeViewItem as if it was a list, irrespective of the state of each TreeViewItem.

TreeView.getRow(TreeItem) gets me an index, but if a leaf is hidden that index changes.

The reason I am looking for this solution is because I am building other lists while I build the TreeView that I would like to reference based on the selection in the TreeView.

Below is a very simple example of what I am trying to acheive:


import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class TreeViewIndexTest extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception{

        TreeItem root = new TreeItem("root");
        List<String> list = new ArrayList();
        int indexCounter = 0;
        for (int i = 0; i < 3; i  ){
            TreeItem parentItem = createBranch("TreeItem "   String.valueOf(indexCounter),root);
            list.add("List Index "   String.valueOf(indexCounter));
            for (int j = 0; j < 3; j  ){
                indexCounter  ;
                createBranch("TreeItem "   String.valueOf(indexCounter),parentItem);
                list.add("List Index "   String.valueOf(indexCounter));
            }
            indexCounter  ;
        }

        TreeView<String> treeView = new TreeView<>(root);

        treeView.getSelectionModel().selectedItemProperty().addListener((v,oldValue,newValue) -> {
            TreeItem<String> selectedItem = treeView.getSelectionModel().getSelectedItem();

            if (selectedItem == null || selectedItem == root)
                return;

            int index = treeView.getRow(selectedItem);

            System.out.println("Index: "   index   "TreeView Item: "   newValue.getValue()   " --- List Value: "   list.get(index));

            //
            //      How can I always return the correct list index based on the selected TreeItem?
            //
            //      The above code does not work
            //

        });

        BorderPane pane = new BorderPane();
        pane.setCenter(treeView);
        Scene scene = new Scene(pane);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    public TreeItem<String> createBranch(String title, TreeItem parent){
        CheckBoxTreeItem newBranch = new CheckBoxTreeItem(title);
        parent.getChildren().add(newBranch);

        return newBranch;
    }

    public static void main(String[] args) {

        launch(args);

    }
}

Output if I select the 7th item with the first two leafs open:

Index: 6TreeView Item: TreeItem 5 --- List Value: List Index 6

Output if I select the same item with the first leaf closed:

Index: 3TreeView Item: TreeItem 5 --- List Value: List Index 3

My guess is that there is another way of going about this, because it must be very common for TreeView selections to access other lists of data.

Any help and examples are greatly appreciated.

CodePudding user response:

My first instinct is to say that you're trying to solve the wrong problem. This sort of relationship between data should probably be handled in the model, or even the view model (if you have those), not the view. I suspect the TreeView#getRow(TreeItem) method is meant mostly to allow creating a MultipleSelectionModel implementation, as that API works on indices (as well as items), and to allow TreeCells to report a meaningful index; a tree structure does not really lend itself to index-based access, but the visual representation of this sort of tree is essentially just a list.

That said, it's not too difficult to write your own method to ignore the "expanded" state of the items.

// gets the row *index* (zero-based), with root being row 0
public static int getRow(TreeItem<?> item) {
  int index = 0;

  TreeItem<?> parent = item.getParent();
  TreeItem<?> current = item;
  while (parent != null) {
    index  ; // account for parent
    // account for any siblings appearing before current item
    for (int i = 0; i < parent.getChildren().indexOf(current); i  ) {
      index  ; // account for previous sibling itself
      // account for descendants of previous sibling, if any
      index  = countDescendants(parent.getChildren().get(i));
    }
    // move up to previous level of tree
    current = parent;
    parent = current.getParent();
  }

  return index;
}

private static int countDescendants(TreeItem<?> item) {
  int count = item.getChildren().size();
  for (TreeItem<?> child : item.getChildren()) {
    count  = countDescendants(child);
  }
  return count;
}

Note the TreeView#getRow(TreeItem) implementation, at least in JavaFX 18.0.1, also calculates the row on-demand (i.e, it's not cached anywhere).

  • Related