在 JavaFX GUI 设计中利用观察者模式 [英] Leveraging the observer pattern in JavaFX GUI design

查看:43
本文介绍了在 JavaFX GUI 设计中利用观察者模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经概括了下面的程序,更新到 Java 8 并删除对现在 已弃用的构建器 API 的依赖.在下面的变体中,

  • A 名为 meters 的 DoubleProperty 用作应用程序的 Observable model.

  • Control,例如TextFieldComboBoxSlider,每个功能都作为一个view模型的em>,以及为用户提供一种控制交互的方式.

  • 一个ConversionPanel中,添加到ComboBoxInvalidationListener更新TextField view model 根据需要反映当前选择的Unit;添加到 TextField 的类似侦听器会在用户键入时更新 model 本身.

  • 相同的模型SliderConversionPanel实例之间共享,链接滑块以及任何监听模型的控件.

    slider.valueProperty().bindBidirectional(meters);

  • 每个ComboBox还有一个模型ObservableList,用户可以从中选择Unit的实例.

代码:

/** 版权所有 (c) 1995、2013,Oracle 和/或其附属公司.版权所有.**以源代码和二进制形式重新分发和使用,有或没有* 修改,在满足以下条件的情况下是允许的* 满足:** - 源代码的重新分发必须保留上述版权* 注意,此条件列表和以下免责声明.** - 以二进制形式重新分发必须复制上述版权* 注意,此条件列表和以下免责声明在* 随分发提供的文档和/或其他材料.** - 既不是 Oracle 的名称,也不是其名称* 贡献者可用于认可或推广衍生产品* 未经本软件事先书面许可.** 本软件由版权所有者和贡献者作为* 是"以及任何明示或暗示的保证,包括但不限于,* 对特定商品的适销性和适用性的默示保证* 目的不明确.在任何情况下,版权所有者或* 贡献者对任何直接的、间接的、偶然的、特殊的、* 示范性或后果性损害(包括但不限于,* 替代品或服务的采购;丢失使用、数据或* 利润;或业务中断)无论是根据* 责任,无论是合同责任、严格责任还是侵权责任(包括* 疏忽或以其他方式)因使用本产品而以任何方式产生* 软件,即使被告知存在此类损坏的可能性.*/包转换器;/*** @see http://docs.oracle.com/javafx/2/swing/port-to-javafx.htm*/公共类单元{字符串描述;双倍乘数;单位(字符串描述,双倍乘数){极好的();this.description = 描述;this.multiplier = 乘数;}@覆盖公共字符串 toString() {字符串 s = "米/" + 描述 + " = " + 乘数;返回 s;}}/** 版权所有 (c) 2012、2013 Oracle 和/或其附属公司.版权所有.* 请勿更改或删除版权声明或此文件标题.*/包转换器;/*** @see http://docs.oracle.com/javafx/2/swing/port-to-javafx.htm*/导入 java.text.NumberFormat;导入 javafx.beans.InvalidationListener;导入 javafx.beans.Observable;导入 javafx.beans.property.DoubleProperty;导入 javafx.collections.ObservableList;导入 javafx.scene.control.*;导入 javafx.scene.layout.HBox;导入 javafx.scene.layout.VBox;导入 javafx.util.StringConverter;公共类 ConversionPanel 扩展 TitledPane {私有静态最终 int MAX = 10000;私有静态最终 int DIGITS = 3;private final TextField textField = new TextField();私人最终滑块滑块 = 新滑块 (0, MAX, 0);私人最终 ComboBox<Unit>组合框;private NumberFormat numberFormat = NumberFormat.getNumberInstance();私人 DoubleProperty 仪表;{numberFormat.setMaximumFractionDigits(DIGITS);}private InvalidationListener fromMeters = (Observable o) ->{如果 (!textField.isFocused()) {textField.setText(numberFormat.format(meters.get()/getMultiplier()));}};private InvalidationListener toMeters = (Observable o) ->{如果 (textField.isFocused()) {尝试 {数字 n = numberFormat.parse(textField.getText());米.set(n.doubleValue() * getMultiplier());} catch(忽略异常){}}};公共转换面板(字符串标题,ObservableList 单位,DoubleProperty 米){设置文本(标题);设置可折叠(假);组合框 = 新组合框<>(单位);组合框.getSelectionModel().select(0);comboBox.setConverter(new StringConverter() {@覆盖公共字符串toString(单位t){返回 t.description;}@覆盖公共单元 fromString(String string) {throw new UnsupportedOperationException("暂不支持.");}});setContent(new HBox(new VBox(textField, slider), comboBox));this.meters = 米;米.addListener(fromMeters);comboBox.valueProperty().addListener(fromMeters);textField.textProperty().addListener(toMeters);滑块.valueProperty().bindBidirectional(meters);fromMeters.invalidated(null);}/*** 返回当前选择的测量单位的乘数.*/公共双 getMultiplier() {返回 comboBox.getValue().multiplier;}}/** 版权所有 (c) 2012、2013 Oracle 和/或其附属公司.版权所有.* 请勿更改或删除版权声明或此文件标题.*/包转换器;导入 javafx.application.Application;导入 javafx.beans.property.DoubleProperty;导入 javafx.beans.property.SimpleDoubleProperty;导入 javafx.collections.FXCollections;导入 javafx.collections.ObservableList;导入 javafx.scene.Scene;导入 javafx.scene.layout.VBox;导入 javafx.stage.Stage;/*** @see https://stackoverflow.com/a/31909942/230513* @see http://docs.oracle.com/javafx/2/swing/port-to-javafx.htm*/公共类转换器扩展应用程序{公共静态无效主(字符串 [] args){发射(参数);}私有最终 ObservableList公制距离;私有最终 ObservableList美国距离;私有最终 DoubleProperty 米 = 新 SimpleDoubleProperty(1);公共转换器(){//为公制距离创建Unit对象,然后//使用这些单位实例化一个 ConversionPanel.metricDistances = FXCollections.observableArrayList(新单位(厘米",0.01),新单位(米",1.0),新单位(公里",1000.0));//为美国距离创建单位对象,然后//使用这些单位实例化一个 ConversionPanel.usaDistances = FXCollections.observableArrayList(新单位(英寸",0.0254),新单位(英尺",0.3048),新单位(码",0.9144),新单位(英里",1609.34));}@覆盖公共无效开始(阶段阶段){stage.setScene(新场景(新VBox(new ConversionPanel("公制", metricDistances, 米),new ConversionPanel("美国系统", usaDistances, 米))));舞台表演();}}

As noted here in the context of Swing, GUI design makes frequent use of the observer pattern. Having frequently used the scheme prescribed in EventListenerList, is there a Java FX example, such as Converter, that focuses on the pattern itself?

解决方案

As noted here, the JavaFX architecture tends to favor binding GUI elements via classes that implement the Observable interface. Toward this end, Irina Fedortsova has adapted the original Converter to JavaFX in Chapter 5 of JavaFX for Swing Developers: Implementing a Swing Application in JavaFX.

I've recapitulated the program below, updating to Java 8 and removing the dependency on the now deprecated builder API. In the variation below,

  • A DoubleProperty named meters functions as the application's Observable model.

  • Instances of Control, such as TextField, ComboBox and Slider, each function as a view of the model, as well as providing a way for the user to control the interaction.

  • Within a ConversionPanel, an InvalidationListener added to the ComboBox updates the TextField view of the model as needed to reflect the currently selected Unit; a similar listener added to the TextField updates the model itself as the user types.

  • The same model is shared between instances of ConversionPanel by the Slider, linking the sliders and any controls listening to the model.

    slider.valueProperty().bindBidirectional(meters);
    

  • Each ComboBox also has a model, ObservableList, from which the user can select among instances of Unit.

Code:

/*
 * 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://stackoverflow.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();
    }
}

这篇关于在 JavaFX GUI 设计中利用观察者模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆