【发布时间】:2015-11-01 19:11:47
【问题描述】:
正如在 Swing 上下文中提到的here,GUI 设计经常使用observer pattern。经常使用EventListenerList 中规定的方案,是否有Java FX 示例,例如Converter,专注于模式本身?
【问题讨论】:
标签: swing javafx observer-pattern observable
正如在 Swing 上下文中提到的here,GUI 设计经常使用observer pattern。经常使用EventListenerList 中规定的方案,是否有Java FX 示例,例如Converter,专注于模式本身?
【问题讨论】:
标签: swing javafx observer-pattern observable
如 here 所述,JavaFX 架构倾向于通过实现 Observable 接口的类来绑定 GUI 元素。为此,Irina Fedortsova 在 JavaFX for Swing Developers 的第 5 章中将原始的 Converter 改编为 JavaFX:Implementing a Swing Application in JavaFX。
我已经概括了下面的程序,更新到Java 8 并删除了对现在deprecated builder API 的依赖。在下面的变体中,
名为meters 的DoubleProperty 用作应用程序的Observable 模型。
Control 的实例,例如 TextField、ComboBox 和 Slider,每个函数都作为 model 的 view,如并为用户提供一种控制交互的方式。
在ConversionPanel内,添加到ComboBox的InvalidationListener会更新模型的TextField视图 em> 根据需要反映当前选中的Unit;添加到TextField 的类似侦听器会在用户键入时更新 模型 本身。
Slider 在ConversionPanel 的 个实例之间共享相同的模型,链接滑块和任何监听模型的控件.
slider.valueProperty().bindBidirectional(meters);
每个ComboBox 也有一个模型,ObservableList,用户可以从中选择Unit 的实例。
代码:
/*
* 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();
}
}
【讨论】: