Java Swing API - 两个JPanel如何相互通信?

时间:2014-09-01 06:40:10

标签: java swing oop jpanel

这是我关于Java Swing的第二篇文章,所以请原谅我,如果我的问题太简单了。我正在尝试让多个JPanels相互通信。我正在构建一个简单的2D网格,我可以添加墙/阻塞单元格,然后运行简单的Floodfill / A *搜索算法(给定开始和目标位置)。

为了解决我的问题,我决定使用一个例子会更容易。所以我创建了一个简单的应用程序,允许用户写一个文本框,提供他/她点击了“开始”按钮。完成对文本框的写入后,用户可以单击“开始”按钮将其翻转为“停止”状态。在“停止”状态下,用户不能向文本框添加任何文本(即应用程序根本不应注册任何键击)。这是一个简单的问题,在这里真正提出了我的问题。这是UI现在的样子:

enter image description here

enter image description here

我的问题: 应该能够在按钮显示“停止”时写入(因为它处于编辑模式)并且我当按钮显示“开始”时,不应能够在文本区域中书写(因为它不是编辑模式)。但是,从上面的图像中,您可以看到我能够在任何情况下在文本区域中书写。如何在按钮状态下编辑依赖文本区域?

这是我的代码,它尝试在按钮面板和文本面板之间设置连接,但它在某种程度上无法按预期工作。

我查看了StackOverflow帖子herehere,但坦率地说,答案对我来说似乎并不清楚。

SimpleTextPanel:

public class SimpleTextPanel extends JPanel implements PropertyChangeListener, ChangeListener {


    private boolean canWrite;
    public SimpleTextPanel() {

        // set the border properties
        canWrite = true;
        TitledBorder title = BorderFactory.createTitledBorder("Simple Text Panel");
        title.setTitleColor(Color.BLACK);
        title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED,
                Color.DARK_GRAY, Color.GRAY));
        this.setBorder(title);

        JTextArea editSpace = new JTextArea(10, 20);
        editSpace.setEditable(true);
        editSpace.addPropertyChangeListener(this);
        this.add(editSpace);
    }

    @Override
    public void stateChanged(ChangeEvent changeEvent) {
        JButton button = (JButton)changeEvent.getSource();
        canWrite = button.getText().equals("Start");
    }

    @Override
    public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
        JTextArea area = (JTextArea)propertyChangeEvent.getSource();
        if(!canWrite) area.setText((String)propertyChangeEvent.getOldValue());
    }
}

SimpleButtonPanel:

public class SimpleButtonPanel extends JPanel implements ActionListener {

    JButton switchButton;
    private boolean canWrite = true;

    public SimpleButtonPanel(SimpleTextPanel txt) {

        // set the border properties
        TitledBorder title = BorderFactory.createTitledBorder("Simple Button Panel");
        title.setTitleColor(Color.BLACK);
        title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED,
                Color.DARK_GRAY, Color.GRAY));
        this.setBorder(title);

        switchButton = new JButton("Start");
        switchButton.addActionListener(this);
        switchButton.addChangeListener(txt);
        this.add(switchButton);
    }

    @Override
    public void actionPerformed(ActionEvent actionEvent) {
        if(switchButton.getText().equals("Start")) {
            switchButton.setText("Stop");
            canWrite = false;
        } else if(switchButton.getText().equals("Stop")) {
            switchButton.setText("Start");
            canWrite = true;
        }
    }
}

SimpleExampleTest:

public class SimpleExampleTest extends JFrame {

    public SimpleExampleTest() {

        setLayout(new BorderLayout());
        setTitle("Simple Example");
        setSize(300, 300);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        SimpleTextPanel text = new SimpleTextPanel();
        SimpleButtonPanel button = new SimpleButtonPanel(text);

        add(text, BorderLayout.NORTH);
        add(button, BorderLayout.SOUTH);
        //button.addPropertyChangeListener(text); // so that text editor knows whether to allow writing or not
    }


    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                SimpleExampleTest ex = new SimpleExampleTest();
                ex.setVisible(true);
            }
        });
    }
}

感谢任何/所有帮助。谢谢!

2 个答案:

答案 0 :(得分:4)

一种方法是设计一个可以由小组共享的模型。

文本面板只想知道当前状态是什么以及该状态何时发生变化。按钮面板想知道状态何时发生变化,并希望能够改变状态。

这使得两个面板彼此分离,因为他们并不真正关心状态是如何改变的,或者只是因为他们可以相应地响应变化...

此外,该模型并不关心它们中的任何一个,它只是携带状态并在变化时提供通知。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.BevelBorder;
import javax.swing.border.TitledBorder;

public class TalkToEachOther {

    public static void main(String[] args) {
        new TalkToEachOther();
    }

    public TalkToEachOther() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Simple Example");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                MutableSimpleModel model = new DefaultSimpleModel();

                SimpleTextPanel text = new SimpleTextPanel(model);
                SimpleButtonPanel button = new SimpleButtonPanel(model);

                frame.add(text, BorderLayout.NORTH);
                frame.add(button, BorderLayout.SOUTH);
                //button.addPropertyChangeListener(text); // so that text editor knows whether to allow writing or not
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface SimpleModel {

        public boolean isEditable();

        public void addPropertyChangeListener(PropertyChangeListener listener);
        public void removePropertyChangeListener(PropertyChangeListener listener);

    }

    public interface MutableSimpleModel extends SimpleModel {

        public void setEditable(boolean editable);

    }

    public class DefaultSimpleModel implements MutableSimpleModel {

        private Set<PropertyChangeListener> listeners;
        private boolean editable;

        public DefaultSimpleModel() {
            listeners = new HashSet<>(25);
        }

        @Override
        public void setEditable(boolean value) {
            if (value != editable) {
                editable = value;
                firePropertyChange("editable", !editable, editable);
            }
        }

        @Override
        public boolean isEditable() {
            return editable;
        }

        @Override
        public void addPropertyChangeListener(PropertyChangeListener listener) {
            listeners.add(listener);
        }

        @Override
        public void removePropertyChangeListener(PropertyChangeListener listener) {
            listeners.remove(listener);
        }

        protected void firePropertyChange(String editable, boolean oldValue, boolean newValue) {
            PropertyChangeEvent evt = new PropertyChangeEvent(this, editable, oldValue, newValue);
            for (PropertyChangeListener listener : listeners) {
                listener.propertyChange(evt);
            }
        }

    }

    public class SimpleTextPanel extends JPanel {

        private SimpleModel model;
        private JTextArea editSpace = new JTextArea(10, 20);

        public SimpleTextPanel(SimpleModel model) {

            this.model = model;
            model.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    updateEditableState();
                }
            });
            setLayout(new BorderLayout());

            // set the border properties
            TitledBorder title = BorderFactory.createTitledBorder("Simple Text Panel");
            title.setTitleColor(Color.BLACK);
            title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED,
                            Color.DARK_GRAY, Color.GRAY));
            this.setBorder(title);

            editSpace = new JTextArea(10, 20);
            this.add(new JScrollPane(editSpace));

            updateEditableState();
        }

        protected void updateEditableState() {
            editSpace.setEditable(model.isEditable());
        }
    }

    public class SimpleButtonPanel extends JPanel implements ActionListener {

        private MutableSimpleModel model;
        private boolean canWrite = true;

        private JButton switchButton;

        public SimpleButtonPanel(MutableSimpleModel model) {

            this.model = model;
            model.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    updateEditableState();
                }
            });

            // set the border properties
            TitledBorder title = BorderFactory.createTitledBorder("Simple Button Panel");
            title.setTitleColor(Color.BLACK);
            title.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED,
                            Color.DARK_GRAY, Color.GRAY));
            this.setBorder(title);

            switchButton = new JButton("Start");
            switchButton.addActionListener(this);
            this.add(switchButton);

            updateEditableState();
        }


        @Override
        public void actionPerformed(ActionEvent actionEvent) {
            model.setEditable(!model.isEditable());
        }

        protected void updateEditableState() {
            if (model.isEditable()) {
                switchButton.setText("Stop");
            } else {
                switchButton.setText("Start");
            }
        }
    }
}

答案 1 :(得分:2)

我建议您创建一个JPanel子类,该子类又包含其他JPanel个对象。通过这种方式,您可以保持对需要交互的GUI元素的引用(即按钮和文本字段)。现在,按钮的ActionListener可以访问JTextField,假设ActionListener是匿名内部类,JTextField是成员变量。

在更复杂的情况下,这种设计可能并不理想。但是,有些概念是相同的。特别是,您需要一个父JPanel来促进孩子JPanel之间的沟通。子JPanel公开了一个接口以允许这种通信。例如,您可以拥有TextFieldPanelenable()disable()方法。