JavaFX TreeView

Este es un control que permite mostrar información de manera jerárquica en una estructura en forma de árbol, por ejemplo, usaremos este control para mostrar y navegar por todas las carpetas y archivos que tengamos almacenados en nuestro disco.

Un TreeItem representa un elemento que podemos agregar a un TreeView, este a su vez puede contener uno o más TreeItem, un TreeView debe tener un TreeItem principal al cual se le llama root node, lo establecemos utilizando el método setRoot(TreeItem root), podemos decidir si mostrar o no el root node, usando el método setShowRoot(false).

@Override
public void start(Stage primaryStage) throws Exception
{
    TreeItem<File> archivos = new TreeItem<>();
    TreeView<File> treeView = new TreeView<>();
    treeView.setShowRoot(false);
    treeView.setRoot(archivos);

    File[] roots = File.listRoots();
    for (File disk : roots)
        archivos.getChildren().add(createNode(disk));

    VBox root = new VBox();
    root.setPadding(new Insets(10.0));
    root.setSpacing(10.0);
    root.getChildren().add(new Label("Sistema de archivos"));
    root.getChildren().add(treeView);

    primaryStage.setTitle("JavaFX TreeView");
    primaryStage.setScene(new Scene(root, 300, 275));
    primaryStage.show();
}

JavaFX Control TreeView

El método estático File.listRoots() devuelve una lista de objetos File que representan los dispositivos de almacenamiento que tenemos disponibles en nuestro computador, por cada uno de ellos agregamos un TreeItem<File> que tiene asociado el File correspondiente.

El método createNode(File) es el encargado de crear los TreeItem<File> para cada uno de ellos sobrescribimos los métodos getChildren() y isLeaf(), el primero es el encargado de generar los nodos hijos y el segundo establece si el nodo actual contiene o no otros hijos.

@Override
public ObservableList<TreeItem<File>> getChildren() {
    if (isFirstTimeChildren) {
        isFirstTimeChildren = false;
        super.getChildren().setAll(buildChildren(this));
    }
    return super.getChildren();
}

La variable isFirstTimeChildren es utilizada para controlar la generación innecesaria de elementos ya que solo lo requerimos en una ocasión, esta ocurrirá cuando el TreeItem sea expandido por primera vez.

private ObservableList<TreeItem<File>> buildChildren(TreeItem<File> TreeItem) {
    File f = TreeItem.getValue();
    if (f != null && f.isDirectory()) {
        File[] files = f.listFiles();
        if (files != null) {
            ObservableList<TreeItem<File>> children = FXCollections.observableArrayList();
            for (File childFile : files) {
                children.add(createNode(childFile));
            }
            return children;
        }
    }
    return FXCollections.emptyObservableList();
}

Este método es el encargado de generar un TreeItem por cada archivo que contenga la carpeta correspondiente, para hacerlo obtenemos el objeto File llamado f, por medio del método TreeView.getValue(), f.listFiles() obtiene la lista de objetos File que representan a los archivos y carpetas que contiene este directorio, creamos un TreeItem<File> para cada uno de ellos y devolvemos la colección de los mismos. 

@Override
public boolean isLeaf() {
    if (isFirstTimeLeaf) {
        isFirstTimeLeaf = false;
        File f = (File) getValue();
        isLeaf = f.isFile();
    }
    return isLeaf;
}

Sobrescribimos el método isLeaf para indicar si el TreeItem es una carpeta o un archivo, si devolvemos un valor true indicará que se trata de un archivo y que el mismo no tiene elementos hijos, de ser un carpeta hacemos lo contrario.

image

Para finalizar veamos como podemos cambiar la apariencia del TreeView estableciendo un nuevo CellFactory esto nos permitirá modificar el texto que aparece en cada TreeItem o agregar un gráfico, por ejemplo una imagen de carpeta o de archivo según corresponda. 

treeView.setCellFactory((TreeView<File> tv) -> {
    return new TreeCell<File>() {
        @Override
        protected void updateItem(File item, boolean empty) {
            super.updateItem(item, empty);
            if(empty) {
                this.setGraphic(null);
                this.setText(null);
            }else {
                this.setGraphic(new OctIconView(item.isFile() ? OctIcon.FILE : OctIcon.FILE_DIRECTORY));
                this.setText(item.toString());
            }
        }
    };
});

image

Los iconos utilizados en este ejemplo pertenecen a la librería FontAwesomeFX que permite insertar iconos de fuentes TTF a una GUI JavaFX.

Proyecto en GitHub: TreeView and TreeItem

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

tkinter Canvas

Histogramas OpenCV Python

Python Binance API