swing javafx observer-pattern observable

swing - Aprovechando el patrón de observador en el diseño de la GUI de JavaFX



observer-pattern observable (1)

Como se señaló here en el contexto de Swing, el diseño de la GUI hace uso frecuente del patrón de observador . Habiendo utilizado con frecuencia el esquema prescrito en EventListenerList , ¿hay algún ejemplo de Java FX, como Converter , que se centre en el patrón en sí?


Como se señaló here , la arquitectura JavaFX tiende a favorecer la unión de elementos de la GUI a través de clases que implementan la interfaz Observable . Con este fin, Irina Fedortsova ha adaptado el Converter original a JavaFX en el Capítulo 5 de JavaFX para desarrolladores de Swing: Implementación de una aplicación Swing en JavaFX .

He recapitulado el programa a continuación, actualizando a Java 8 y eliminando la dependencia de la API del generador ahora en desuso . En la variación a continuación,

  • Una DoubleProperty denominada DoubleProperty funciona como modelo Observable la aplicación.

  • Las instancias de Control , como TextField , ComboBox y Slider , funcionan como una vista del modelo , además de proporcionar una forma para que el usuario controle la interacción.

  • Dentro de un ConversionPanel , un InvalidationListener agregado al ComboBox actualiza la vista TextField del modelo según sea necesario para reflejar la Unit seleccionada actualmente; un oyente similar agregado al TextField actualiza el modelo mismo a medida que el usuario escribe.

  • El control deslizante comparte el mismo modelo entre las instancias de ConversionPanel , que vincula los controles deslizantes y los controles que escuchan el modelo .

    slider.valueProperty().bindBidirectional(meters);

  • Cada ComboBox también tiene un modelo , ObservableList , desde el cual el usuario puede seleccionar entre instancias de Unit .

Código:

/* * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Oracle or the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package converter; /** * @see http://docs.oracle.com/javafx/2/swing/port-to-javafx.htm */ public class Unit { String description; double multiplier; Unit(String description, double multiplier) { super(); this.description = description; this.multiplier = multiplier; } @Override public String toString() { String s = "Meters/" + description + " = " + multiplier; return s; } } /* * Copyright (c) 2012, 2013 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. */ package converter; /** * @see http://docs.oracle.com/javafx/2/swing/port-to-javafx.htm */ import java.text.NumberFormat; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.property.DoubleProperty; import javafx.collections.ObservableList; import javafx.scene.control.*; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.util.StringConverter; public class ConversionPanel extends TitledPane { private static final int MAX = 10000; private static final int DIGITS = 3; private final TextField textField = new TextField(); private final Slider slider = new Slider(0, MAX, 0); private final ComboBox<Unit> comboBox; private NumberFormat numberFormat = NumberFormat.getNumberInstance(); private DoubleProperty meters; { numberFormat.setMaximumFractionDigits(DIGITS); } private InvalidationListener fromMeters = (Observable o) -> { if (!textField.isFocused()) { textField.setText(numberFormat.format(meters.get() / getMultiplier())); } }; private InvalidationListener toMeters = (Observable o) -> { if (textField.isFocused()) { try { Number n = numberFormat.parse(textField.getText()); meters.set(n.doubleValue() * getMultiplier()); } catch (Exception ignored) { } } }; public ConversionPanel(String title, ObservableList<Unit> units, DoubleProperty meters) { setText(title); setCollapsible(false); comboBox = new ComboBox<>(units); comboBox.getSelectionModel().select(0); comboBox.setConverter(new StringConverter<Unit>() { @Override public String toString(Unit t) { return t.description; } @Override public Unit fromString(String string) { throw new UnsupportedOperationException("Not supported yet."); } }); setContent(new HBox(new VBox(textField, slider), comboBox)); this.meters = meters; meters.addListener(fromMeters); comboBox.valueProperty().addListener(fromMeters); textField.textProperty().addListener(toMeters); slider.valueProperty().bindBidirectional(meters); fromMeters.invalidated(null); } /** * Returns the multiplier for the currently selected unit of measurement. */ public double getMultiplier() { return comboBox.getValue().multiplier; } } /* * Copyright (c) 2012, 2013 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. */ package converter; import javafx.application.Application; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.layout.VBox; import javafx.stage.Stage; /** * @see https://.com/a/31909942/230513 * @see http://docs.oracle.com/javafx/2/swing/port-to-javafx.htm */ public class Converter extends Application { public static void main(String[] args) { launch(args); } private final ObservableList<Unit> metricDistances; private final ObservableList<Unit> usaDistances; private final DoubleProperty meters = new SimpleDoubleProperty(1); public Converter() { //Create Unit objects for metric distances, and then //instantiate a ConversionPanel with these Units. metricDistances = FXCollections.observableArrayList( new Unit("Centimeters", 0.01), new Unit("Meters", 1.0), new Unit("Kilometers", 1000.0)); //Create Unit objects for U.S. distances, and then //instantiate a ConversionPanel with these Units. usaDistances = FXCollections.observableArrayList( new Unit("Inches", 0.0254), new Unit("Feet", 0.3048), new Unit("Yards", 0.9144), new Unit("Miles", 1609.34)); } @Override public void start(Stage stage) { stage.setScene(new Scene(new VBox( new ConversionPanel("Metric System", metricDistances, meters), new ConversionPanel("U.S. System", usaDistances, meters)))); stage.show(); } }