JavaFX Observable Collections

El lenguaje de programación Java cuenta con colecciones como: List, Set, Map, la API JavaFX extiende estas colecciones con las interfaces: ObservableList, ObservableSet, ObservableMap, respectivamente, esto con el objetivo de proporcionarle a las colecciones el soporte para la notificación de cambios e invalidación como lo hacen las Propiedades JavaFX.

Estas colecciones se encuentran en el paquete javafx.collections y para crearlas debemos usar la clase FXCollections que nos provee de distintos métodos estáticos para crear la colección que deseemos, veamos un par de ejemplos.

// crear una coleccion vacia
ObservableList<String> textList = FXCollections.observableArrayList();

// crea la coleccion a partir de una lista de elementos
ObservableList<Long> numberList = FXCollections.observableArrayList(1L, 2L, 3L);

Set<Character> set = new HashSet<>();
set.add('a');
set.add('b');
set.add('c');

// crear e inicializar la colleccion con el Set indicado 
ObservableSet<Character> textSet = FXCollections.observableSet(set);

Podemos apreciar las distintas formas de crear la colección, primero creamos un ObservableList<T> donde T es String en nuestro ejemplo, esto indica el tipo de elementos que almacenaremos en la colección, vemos que podemos crear un objeto sin elementos, indicar uno a uno los elementos iniciales o utilizar una colección Java para inicializar el Observable Collection correspondiente.

Recibir notificación de cambios

Para registrar un método que reciba la notificación de cambios en una colección JavaFX usaremos el método addListener() para recibir las notificaciones de invalidación, esta se envían cada vez que cambian los elementos de la colección, por ejemplo, al: agregar, remover, cambiar el valor de un elemento, etc., cuando ya no necesitemos recibir las notificaciones podemos usar el método removeListener().

public static void main(String[] args) {
    ObservableList<Integer> numList = FXCollections.observableArrayList();
    numList.addListener(JavaFXObservableCollections::invalidated);
    
    numList.add(10);
    numList.addAll(100, 200, 300);
    numList.set(2, 500);
    numList.remove(2);                
}

public static void invalidated(Observable list) {
    System.out.println("List is invalid.");
}

En el fragmento de código recibiremos 4 notificaciones, las mismas se producen al usar los métodos: add, addAll, set, remove, debemos aclarar que el método addAll(100, 200, 300) añade 3 elementos a la colección pero solo se produce una notificación de invalidación al utilizarlo.

Cuando deseemos recibir una notificación mas especifica para saber que tipo de cambio se ha hecho y cual ha sido usaremos un ChangeListener, este nos permite saber el tipo de cambio realizado y los elementos que han sido afectados en dicho cambio.

public static void main(String[] args) {
    ObservableList<Integer> numList = FXCollections.observableArrayList();
    numList.addListener(JavaFXObservableCollections::onChanged);
}

public static void onChanged(ListChangeListener.Change<? extends Integer> change) {
    while (change.next()) {
        if (change.wasPermutated()) {
            // permutacion
        } else if (change.wasUpdated()) {
            // actualizacion
        } else if (change.wasReplaced()) {
            // reemplazo
        } else {
            if (change.wasRemoved()) {
                // elemento borrado
            } else if (change.wasAdded()) {
                // elemento agregado
            }
        }
    }
}

Usando change.next() podemos recorrer cada uno de los cambios producidos, para determinar el tipo de cambios disponemos de los métodos change.was*(), en los comentarios del código vemos el tipo de cambio que representa cada método.

Cuando deseemos saber el rango de elemento que ha cambiado usaremos los métodos change.getTo() y change.getFrom() para obtener los índices final e inicial de los elementos que han cambiado.

ObservableList<Integer> numList = FXCollections.observableArrayList();
numList.addListener(JavaFXObservableCollections::onChanged);

numList.add(10);
numList.addAll(100, 200, 300);

// ...
// detectar los cambios al añadir elementos a la coleccion
if (change.wasAdded()) {
    
    int start = change.getFrom();
    int end = change.getTo();
    
    System.out.println("added range: [" + start + " - " + end + "]");
    
    change.getAddedSubList().forEach(System.out::println);
}
// ...

Este ejemplo muestro como detectar cuando se añade un elemento a la colección además de que usamos el método change.getAddedSubList() para obtener la lista de elementos agregados.

salida en consola:

added range: [0 - 1]
10
added range: [1 - 4]
100
200
300

Podemos hacer lo mismo con los otros cambios, por ejemplo, para obtener la lista de elementos borrados solo debemos usar change.getRemoved() o podemos crear la correspondiente lista usando los índices obtenidos.

Las coleccionas JavaFX ObservableSet y ObservableMap son más simples, estas utilizan las clases, SetChangeListener.Change y MapChangeListener.Change respectivamente, ambas cuentan con los métodos wasAdded() y wasRemoved() que devuelve true si se añadido o removido un elemento de la colección, también disponemos de los correspondientes métodos para obtener la lista de elementos agregados o removidos.

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Entrenar OpenCV en Detección de Objetos

Detección de figuras geométricas

Procesamiento de imágenes en OpenCV

Conociendo la clase cv::Mat de OpenCV