子GUI组件应如何(通过MVC)访问其父项? [英] How should child GUI components access their parents (with MVC)?

查看:69
本文介绍了子GUI组件应如何(通过MVC)访问其父项?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我正在构建Java Swing GUI,并且有一个框架,其中包含一个面板,该面板包含另一个面板,该面板包含一个按钮. (假设这些面板是可重用的,所以我将它们划分为单独的类.)

Let's say I'm building a Java Swing GUI, and I've got a frame which contains a panel, containing another panel, which contains a button. (Assume the panels are reusable, and so I've made them into individual classes.)

Frame → FirstPanel → SecondPanel → Button

实际上,孩子的队伍可能更复杂,但我只是想保持这个示例简单.

In reality, the line of children could be more complex, but I just want to keep this example simple.

如果我希望按钮控制其父组件之一(例如调整框架的大小),那么在两个不一定要直接位于另一个GUI类之间的GUI类之间实现功能的最佳方法是什么?

If I want the button to control one of its parent components (eg. resize the frame), what is the best way to implement functionality between two GUI classes that aren't necessarily one directly inside the other?

我不喜欢将getParent()方法串联在一起的想法,或者不希望将Frame的实例一直传递通过其子级以便可以从SecondPanel访问它的想法.基本上,我不想以一种或另一种方式将我的课程以菊花链方式链接在一起.

I don't like the idea of stringing getParent() methods together, or of passing the instance of Frame all the way down through its children so that it can be accessed from SecondPanel. Basically, I don't want to daisy-chain my classes together one way or another.

这是按钮应该在其中更新模型而不是直接在父组件上进行更新的实例吗?然后,父级会收到模型更改的通知,并相应地进行更新?

Is this an instance in which the button should be updating a model and not the parent component directly? Then the parent is notified of the model's change and updates itself accordingly?

我整理了一个小示例,该示例应自行编译并运行以说明我的问题.这是一个JPanel中的两个JButton,另一个JPanel中的一个JFrame.这些按钮控制JFrame的大小.

I've put together a little example that should compile and run on its own to illustrate my problem. This is two JButtons in a JPanel, in another JPanel, in a JFrame. The buttons control the size of the JFrame.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class MVCExample
{
    public static void main(String[] args)
    {
        Model model = new Model();

        Controller ctrl = new Controller();
        ctrl.registerModel(model);

        View view = new View(ctrl);
        view.setVisible(true);

        model.init();
    }

    /**
     * Model class
     */
    static class Model
    {
        private ArrayList<PropertyChangeListener> listeners =
                new ArrayList<PropertyChangeListener>();

        private Dimension windowSize;

        public Dimension getWindowSize(){ return windowSize; }

        public void setWindowSize(Dimension windowSize)
        {
            if(!windowSize.equals(getWindowSize()))
            {
                firePropertyChangeEvent(getWindowSize(), windowSize);
                this.windowSize = windowSize;
            }
        }

        public void init()
        {
            setWindowSize(new Dimension(400, 400));
        }

        public void addListener(PropertyChangeListener listener)
        {
            listeners.add(listener);
        }

        public void firePropertyChangeEvent(Object oldValue, Object newValue)
        {
            for(PropertyChangeListener listener : listeners)
            {
                listener.propertyChange(new PropertyChangeEvent(
                        this, null, oldValue, newValue));
            }
        }
    }

    /**
     * Controller class
     */
    static class Controller  implements PropertyChangeListener
    {
        private Model model;
        private View view;

        public void registerModel(Model model)
        {
            this.model = model;
            model.addListener(this);
        }

        public void registerView(View view)
        {
            this.view = view;
        }

        // Called from view
        public void updateWindowSize(Dimension windowSize)
        {
            model.setWindowSize(windowSize);
        }

        // Called from model
        public void propertyChange(PropertyChangeEvent pce)
        {
            view.processEvent(pce);
        }
    }

    /**
     * View classes
     */
    static class View extends JFrame
    {
        public View(Controller ctrl)
        {
            super("JFrame");

            ctrl.registerView(this);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            getContentPane().add(new FirstPanel(ctrl));
            pack();
        }

        public void processEvent(PropertyChangeEvent pce)
        {
            setPreferredSize((Dimension)pce.getNewValue());
            pack();
        }
    }

    static class FirstPanel extends JPanel
    {
        public FirstPanel(Controller ctrl)
        {
            setBorder(BorderFactory.createTitledBorder(
                    BorderFactory.createLineBorder(
                    Color.RED, 2), "First Panel"));

            add(new SecondPanel(ctrl));
        }
    }

    static class SecondPanel extends JPanel
    {
        private Controller controller;
        private JButton smallButton = new JButton("400x400");
        private JButton largeButton = new JButton("800x800");

        public SecondPanel(Controller ctrl)
        {
            this.controller = ctrl;
            setBorder(BorderFactory.createTitledBorder(
                    BorderFactory.createLineBorder(
                    Color.BLUE, 2), "Second Panel"));

            add(smallButton);
            add(largeButton);

            smallButton.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent ae)
                {
                    controller.updateWindowSize(new Dimension(400, 400));
                }
            });

            largeButton.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent ae)
                {
                    controller.updateWindowSize(new Dimension(800, 800));
                }
            });
        }
    }
}

我不喜欢的是,控制器需要存在于JFrame中,以便框架可以自行注册以接收事件.但是随后必须将控制器一直传递到SecondPanel(112、131和143行),以便面板可以与模型进行通信.

What I don't like, is that the controller needs to exist in the JFrame so that the frame can register itself to receive events. But the controller then has to be passed all the way down to the SecondPanel (lines 112, 131, and 143) so that the panel can communicate with the model.

我觉得这里发生了一些效率低下的事情(并且各类之间的耦合变得太紧密了).如果我的问题不清楚,请告诉我.

I feel like there is something inefficient going on here (and the classes become too tightly coupled). Let me know if my problem isn't clear.

推荐答案

在Swing中,控制器和视图通常属于UI委托,并且模型是独立的.该视图可以构造复杂的组件层次结构来表示模型,并且控制器会在必要时侦听它们.该组件仅用于将两个部分联系在一起的各种簿记.

In Swing, the controller and view typically belong to the UI delegate, and the model is separate. The view can construct a complex hierarchy of components to represent the model, and the controller listens on them as necessary. The component is just used for the various bookkeeping that ties together the two parts.

例如,在组合框中,可以在JCombobox中设置UI和模型. ComboboxUI组装组成组合框的组件-渲染器或编辑器和按钮,以及弹出窗口和列表-并提供布局和可能的自定义渲染.这是视图逻辑.它还会侦听所有这些组件,并根据需要修改模型.这是控制器级别.对模型的更改会通过事件传播到组件.

So, for example, in a combobox, the JCombobox is where you set the UI and the model. The ComboboxUI assembles the components that make up the combobox - the renderer or editor and the button, as well as the popup and list - and provides layout and possibly custom rendering. This is the View logic. It also listens on all those components and modifies the model as appropriate. This is the Controller level. The changes to the model bubble up to the component through events.

因此,在您的情况下,没有理由视图代码无法构建整个组件层次结构.我希望模型为更改其自身属性的按钮提供操作,然后让视图侦听该属性更改并调整窗口大小:

So, in your case, there is no reason the view code can't build the whole component hierarchy. I would have the model provide Actions for the buttons that change its own property, and then have the view listen on that property change and resize the window:

class View implements PropertyChangeListener {
    JFrame frame;

    View(Model model) {
        model.addPropertyChangeListener(this);

        frame = new JFrame();

        List<Action> actions = model.getActions();

        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(1, actions.size()));

        for(Action action : actions) {
            panel.add(new JButton(action));
        }

        frame.getContentPane().add(panel);
        frame.pack();
        frame.setVisible(true);
    }

    public void propertyChange(PropertyChangeEvent evt) {
        frame.setSize((Dimension)evt.getNewValue())
    }
}

class Model {
    List<Action> actions = new ArrayList<Action>();
    Dimension dimension;

    Model() {
        actions.add(new DimensionAction(400, 400));
        actions.add(new DimensionAction(800, 800));
    }

    List<Action> getActions() {
        return Collections.unmodifiableList(actions);
    }

    void setDimension(Dimension newDimension) {
        Dimension oldDimension = this.dimension;
        this.dimension = newDimension;

        firePropertyChange("dimension", oldDimension, newDimension);
    }

    ... Property change support ...

    class DimensionAction extends AbstractAction {
        Dimension dimension;

        DimensionAction(int width, int height) {
            super(width + "x" + height);
            this.dimension = new Dimension(width, height);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            Model.this.dimension = dimension;
        }
    }
}

这篇关于子GUI组件应如何(通过MVC)访问其父项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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