简体   繁体   English

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

[英]Java Swing API - How Can Two JPanels Communicate With Each Other?

This is my second post related to Java Swing, so pardon me if my question is too straightforward. 这是我的第二篇有关Java Swing的文章,如果我的问题太简单了,请原谅。 I'm trying to get multiple JPanels to communicate with each other. 我正在尝试使多个JPanels相互通信。 I'm building a simple 2D grid that I can add walls/blocked cells to, and then run a simple Floodfill/A* Search algorithm on (given start and goal locations). 我正在构建一个简单的2D网格,可以向其中添加墙/被阻止的单元格,然后在给定的开始和目标位置上运行简单的Floodfill / A *搜索算法。

To isolate my issue, I decided that working with an example would be easier. 为了隔离我的问题,我认为使用示例会更容易。 So I created a simple application, that allows the user to write to a text box, provided that he/she has clicked on a "Start" button. 因此,我创建了一个简单的应用程序, 只要他/她单击了“开始”按钮,就允许用户写入文本框。 Once writing to the text box is done, the user can click on the "Start" button to flip it to the "Stop" state. 写入文本框后,用户可以单击“开始”按钮将其翻转到“停止”状态。 In the "Stop" state, the user cannot add any text to the text box (ie the application should not register any keystrokes at all). 在“停止”状态下,用户无法在文本框中添加任何文本(即,应用程序根本不应注册任何击键)。 This is a simple problem that really brings out my question here. 这是一个简单的问题,确实在这里提出了我的问题。 Here's how the UI looks like right now: 现在,UI如下所示:

在此处输入图片说明

在此处输入图片说明

My question: I should be able to write when the button shows "Stop" (since it is in edit mode) and I shouldn't be able to write in the text area when the button shows "Start" (since it is not it edit mode). 我的问题:应该能够编写按钮时,显示“停止”(因为它是在编辑模式下),我不应该能够在文本区域时写的按钮显示“开始”(因为它是不是编辑模式)。 However, from the above images, you can see that I'm able to write in the text area in any case. 但是,从以上图像中,您可以看到无论如何我都可以在文本区域中书写。 How do I then make the editing of the text area dependent on the button state? 我如何再进行文本区域的编辑依赖于按键的状态?

Here's my code which attempts at setting up that connection between the button panel and the text panel, but it somehow isn't working as expected. 这是我的代码,试图在按钮面板和文本面板之间建立该连接,但是以某种方式无法正常工作。

I looked at StackOverflow posts here and here , but frankly, the answers didn't seem clear to me. 我在这里这里都看过StackOverflow的帖子,但是坦率地说,答案对我来说似乎并不明确。

SimpleTextPanel: 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: 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: 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);
            }
        });
    }
}

Any/all help is appreciated. 任何/所有帮助表示赞赏。 Thank you! 谢谢!

One approach would be to devise a model that could shared by the panels. 一种方法是设计一个小组可以共享的模型。

The text panel only wants to know what the current state is and when that state changes. 文本面板只想知道当前状态以及状态何时更改。 The button panel wants to know when the state changes and also wants to be able to change the state. 按钮面板希望知道状态何时更改,还希望能够更改状态。

This decouples the two panels from each other, as they don't really care how the state is changed or who by, only that they can respond to the changes accordingly... 这使两个面板彼此分离,因为它们并不真正在乎状态如何更改或由谁改变,而只是他们可以相应地响应更改...

Also, the model doesn't care about either of them, it just carries the state and provides notification when it changes. 而且,该模型并不关心它们中的任何一个,它只是携带状态并在更改时提供通知。

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");
            }
        }
    }
}

I suggest that you create a single JPanel subclass which in turn contains other JPanel objects. 我建议您创建一个JPanel子类,该子类又包含其他JPanel对象。 In this way, you can keep references to the GUI elements that need to interact (ie the button and the text field). 这样,您可以保留对需要交互的GUI元素(即按钮和文本字段)的引用。 Now the ActionListener of the button can access the JTextField , assuming that the ActionListener is an anonymous inner class and that the JTextField is a member variable. 现在,假设ActionListener是匿名内部类并且JTextField是成员变量,则按钮的ActionListener可以访问JTextField

In a more complex situation, this design might not be ideal. 在更复杂的情况下,这种设计可能不是理想的。 Some of the concepts will be the same, though. 不过,其中一些概念是相同的。 In particular, you need a parent JPanel which facilitates communication between the children JPanel s. 特别是,您需要一个父级JPanel ,以方便子级JPanel之间的通信。 The children JPanel s expose an interface to allow this communication. 子级JPanel公开一个接口以允许此通信。 For example you could have a TextFieldPanel which has enable() and disable() methods. 例如,您可能有一个具有enable()disable()方法的TextFieldPanel

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM