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

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

问题描述

正如在Swing上下文中指出的那样



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




  • A DoubleProperty 名为作为应用程序的 Observable 模型


  • 实例 Control ,例如 TextField ComboBox Slider ,每个函数作为模型视图,并为用户提供控制交互的方式。


  • ConversionPanel 中, InvalidationListener 添加到 ComboBox 更新 TextField 视图 模型根据需要反映当前选择的单位;添加到 TextField 的类似侦听器会在用户输入时更新模型


  • 相同的模型 ConversionPanel 的实例之间由 Slider <共享 / code>,链接滑块和任何控制模型的控件

     滑块。.valueProperty()bindBidirectional(米); 


  • 每个 ComboBox 也有一个 model ObservableList ,用户可以从中选择 Unit




代码:

  / * 
*版权所有(c)1995,2013,Oracle和/或其附属公司。版权所有。
*
*如果满足以下条件
*,则允许以源代码和二进制形式重新分发和使用(包括或不使用
*修改):
*
* - 源代码的重新分发必须保留上述版权
*通知,此条件列表和以下免责声明。
*
* - 二进制形式的再分发必须在
*文件和/或随附的其他材料中复制上述版权
*通知,此条件清单和以下免责声明分布。
*
* - 未经事先书面许可,Oracle软件名称或其
*贡献者的名称均不得用于支持或宣传从该软件衍生的产品
*。
*
*本软件由版权所有者和贡献者提供as b $ b * is以及任何明示或暗示的担保,包括但不限于
*暗示对于适销性和特定
*目的的适用性的保证不承担任何责任。在任何情况下,版权所有者或
*贡献者均不对任何直接,间接,偶然,特殊,
*示范或间接损害(包括但不限于
*采购)承担责任取消商品或服务;损失使用,数据,或b $ b *利润;或业务中断)以及任何理由的b $ b *责任,无论是合同,严格责任还是侵权(包括$) b $ b *疏忽或其他情况)以任何方式使用此
*软件,即使已被告知此类损害的可能性。
* /
包转换器;

/ **
* @see http://docs.oracle.com/javafx/2/swing/port-to-javafx.htm
* /
公共类单位{

字符串描述;
倍乘数;

单位(字符串描述,双倍乘数){
super();
this.description = description;
this.multiplier = multiplier;
}

@Override
public String toString(){
String s =Meters /+ description +=+ multiplier;
返回s;
}
}

/ *
*版权所有(c)2012,2013 Oracle和/或其附属公司。版权所有。
*请勿更改或删除版权声明或此文件标题。
* /
包转换器;

/ **
* @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;

公共类ConversionPanel扩展名为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>组合框;
private NumberFormat numberFormat = NumberFormat.getNumberInstance();
私人DoubleProperty米;

{
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(忽略异常){
}
}
};

public ConversionPanel(String title,ObservableList< Unit> units,DoubleProperty米){
setText(title);
setCollapsible(false);
comboBox = new ComboBox<>(单位);
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);
}

/ **
*返回当前所选测量单位的乘数。
* /
public double getMultiplier(){
return comboBox.getValue()。multiplier;
}
}

/ *
*版权所有(c)2012,2013 Oracle和/或其附属公司。版权所有。
*请勿更改或删除版权声明或此文件标题。
* /
包转换器;

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){
发射(参数);
}

私人最终ObservableList< Unit> metricDistances;
私人最终ObservableList< Unit> usaDistances;
私人最终DoubleProperty米=新的SimpleDoubleProperty(1);

public Converter(){
//为度量距离创建单位对象,然后
//用这些单位实例化一个ConversionPanel。
metricDistances = FXCollections.observableArrayList(
新单位(厘米,0.01),
新单位(米,1.0),
新单位(公里,1000.0) ));

//为美国距离创建单位对象,然后
//使用这些单位实例化一个ConversionPanel。
usaDistances = FXCollections.observableArrayList(
新单位(英寸,0.0254),
新单位(英尺,0.3048),
新单位(码,0.9144 ),
新单位(里程,1609.34));
}

@Override
public void start(阶段阶段){
stage.setScene(新场景(新的VBox)(
new ConversionPanel(Metric)系统,metricDistances,米,
新的ConversionPanel(US System,usaDistances,meter))));
stage.show();
}
}


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天全站免登陆