提问者:小点点

利用JavaFXGUI设计中的观察者模式


正如这里在Swing的上下文中所指出的,GUI设计经常使用观察者模式。经常使用EventListenerList中规定的方案后,是否有一个Java的FX示例,例如Converter,专注于模式本身?


共1个答案

匿名用户

正如这里所指出的,JavaFX架构倾向于通过实现可观察接口的类绑定GUI元素。为此,Irina Fedortsova在《JavaFX for Swing Developers:在JavaFX中实现Swing应用程序》第5章中将原始的Converter改编为JavaFX。

我总结了下面的程序,更新到Java8,并删除了对现已弃用的构建器API的依赖。在下面的变体中,

>

  • 名为DoubleProperty用作应用程序的可观察模型。

    Control的实例,例如TextFieldComboBoxSlider,每个功能都作为模型的视图,以及为用户提供控制交互的方式。

    ConversionPanel中,添加到ComboBoxInvalidationListener根据需要更新模型的TextField视图,以反映当前选择的Unit;添加到TextField的类似侦听器会随着用户键入而更新模型本身。

    SliderConversionPanel的实例之间共享相同的模型,链接滑块和监听模型的任何控件。

      slider.valueProperty().bindBidirectional(meters);
    

    每个ComboBox也有一个模型,可观察列表,用户可以从中选择Unit的实例。

    代码:转换器/Unit.java

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

    代码:转换器/ConversionPanel.java

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

    代码:转换器/Converter.java

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