Skip to content

6 Manejo de cambios

Propiedades

Una propiedad en JavaFX es un tipo especial de variable de un objeto JavaFX. Las propiedades JavaFX son usadas para almacenar información de un objeto. Además, las propiedades tienen la capacidad de notificar cambios de sus valores y también permiten hacer asociaciones con propiedades de otros los objetos. Las propiedades deben ser clases que implementen la interfaz ObservableValue.

ObservableValue es una interfaz que representa un valor observable, es decir, un valor cuyo cambio puede ser observado por otros objetos. Esta interfaz se encuentra en el paquete javafx.beans.value y es fundamental para implementar el patrón de diseño Observer/Observable en el contexto de las propiedades observables.

JavaFX proporciona varias implementaciones de ObservableValue para diferentes tipos de propiedades, como BooleanProperty, IntegerProperty, DoubleProperty, StringProperty, etc. Es ampliamente utilizado en JavaFX para sincronizar automáticamente los cambios de estado de los datos con la interfaz de usuario, asegurando que los componentes gráficos reflejen siempre el estado actual de los datos.

A continuación se presentan algunos métodos comunes que se encuentran en la interfaz ObservableValue:

  • addListener(ChangeListener<? super T> listener): Permite registrar un ChangeListener que será notificado cuando el valor observado cambie.
  • removeListener(ChangeListener<? super T> listener): Permite eliminar un ChangeListener registrado previamente.
  • getValue(): Retorna el valor actual del ObservableValue.
  • setValue(T value): Cambia el valor de la propiedad al valor dado.

Algunos componentes, como CheckBox o RadioButton utilizan estas propiedades internamente. También propiedades como Label o TextField las contiene. Para acceder a ellas, como valores observables, se puede acceder a través de los métodos xxxProperty() donde xxx es el tipo de dato. Por ejemplo, para acceder al texto observable de un Label, se accedería a través de textProperty().

También es posible asociar propiedades entre sí, de tal forma que cuando se produzca un cambio en alguna propiedad, automáticamente se cambie la propiedad asociada, es decir, cuando dos variables están asociadas, un cambio de una se ve reflejada en la otra. Para asociar una propiedad a otra es necesario utilizar el método bind(ObjectValue<? extends T> property) de las propiedades. Veamos un ejemplo:

Slider slider = new Slider(0, 1, 0);

ProgressBar bar = new ProgressBar(0);
bar.setMaxWidth(Double.MAX_VALUE);
bar.progressProperty().bind(slider.valueProperty);

En el ejemplo anterior, se está asociando el valor del slider a la barra de progreso, por lo que cuando cambie el valor del slider, también cambiará el valor de la barra de progreso. En este caso, estamos ante una asociación unidireccional, es decir, los cambios de una propiedad se ve reflejada en la otra, pero no al revés. Para que una asociación sea bidireccional es necesario utilizar el método bindDirectional(Property<T> property). Por el contrario, se puede desasociar dos propiedades, haciendo uso del método unbind() para asociaciones unidireccionales y unbindBidirectional(Property<T> other) para bidireccionales.

Para hacer cualquier objeto en JavaFX como un valor observable, puedes utilizar las clases provistas en el paquete javafx.beans.property, como SimpleObjectProperty o ReadOnlyObjectWrapper. Estas clases permiten envolver un objeto y proporcionar métodos para acceder y modificar ese objeto de manera observable, es decir, permiten detectar cambios en su valor y notificar a los listeners registrados. SimpleObjectProperty es mutable, es decir, permite lectura y escritura, mientras que ReadOnlyObjectWrapper es inmutable, es decir, solo permite lectura.

ChangeListener

ChangeListener es una interfaz funcional y genérica que se utiliza para escuchar cambios en un ObservableValue. Esta interfaz permite detectar y manejar eventos cuando el valor de una propiedad observable cambia. En el caso específico de un CheckBox, podemos utilizar un ChangeListener para ser notificados cuando el estado seleccionado del CheckBox cambia.

El método de dicha interfaz es change(ObservableValue<? extends T> observable, T oldValue, T newValue), que recibe el objeto observable cuyo valor ha cambiado, el viejo valor y el nuevo valor.

Cuando se registra un ChangeListener a un ObservableValue, el método changed se ejecutará cada vez que el valor del ObservableValue cambie. Dentro del método changed, puedes implementar la lógica para manejar y responder al cambio de valor. Por ejemplo, actualización de la interfaz de usuario, procesamiento de datos, o cualquier acción basada en el cambio detectado.

IntegerProperty counter = new SimpleIntegerProperty(0);

counter.addListener(new ChangeListener<Number>() {
    @Override
    public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
        System.out.println("Contador ha cambiado de " + oldValue + " a " + newValue);
    }
});

counter.set(1); 
counter.set(5); 

Colecciones JavaFX

En JavaFX, existe el paquete javafx.collections que reúne una serie de interfaces y clases para las colecciones de JavaFX, entre ellas encontramos:

  • ObservableList: Interfaz que representa una lista que permite a los listeners detectar los cambios que producen.
  • ListChangeListener: Interfaz que recibe las notificaciones de cambios de un ObservableList
  • FXCollections: Clase de utilidad con métodos estáticos que son copias de los métodos de la clase java.util.Collections.
  • ListChangeListener.Change: Clase que representa un cambio realizado por un ObservableList.

Existe clases e interfaces parecidas para mapas y conjunto observables. Sus nombres son parecidos a lo de las listas observables, pero el lugar de usar List se usaría Map o Set, respectivamente. Por ejemplo, existe la interfaz ObservableMap para mapas y ObservableSet para conjuntos.

En el siguiente ejemplo, se observa los cambios desarrollados en una lista:

List<String> list = new ArrayList<String>();

ObservableList<String> observableList = FXCollections.observableList(list);

observableList.addListener(new ListChangeListener(){
    @Override
    public void onChanged(ListChangeListener.Change change){
        System.out.println("Detected change!");
    }
});

Hasta ahora, hemos visto algunos componentes donde era necesario indicar los elementos de dicho componente como una lista observable.

Cuando se modifica la lista (se añaden, eliminan o modifican elementos), se generan eventos de cambio que notifican a los listeners sobre los detalles específicos de la modificación.

ObservableList está diseñada para ser utilizada en conjunto con componentes gráficos de JavaFX, como ListView, TableView, ComboBox, etc., para mantener la sincronización automática entre los datos y la interfaz de usuario.

A continuación se presentan algunos métodos comunes que se encuentran en la interfaz ObservableList:

  • addListener(InvalidationListener listener): Permite registrar un InvalidationListener que será notificado cuando la lista se invalide. Esto significa que la lista ha cambiado estructuralmente de alguna manera y puede requerir que se vuelva a consultar.
  • addListener(ListChangeListener<? super E> listener): Permite registrar un ListChangeListener que será notificado cuando se realicen cambios estructurales en la lista, como añadir, eliminar o reordenar elementos.
  • removeListener(InvalidationListener listener): Permite eliminar un InvalidationListener registrado previamente.
  • removeListener(ListChangeListener<? super E> listener): Permite eliminar un ListChangeListener registrado previamente.
  • addAll(E... elements): Añade todos los elementos especificados al final de la lista.
  • removeAll(E... elements): Elimina todos los elementos especificados de la lista, si están presentes.
  • clear(): Elimina todos los elementos de la lista.