从子类的线程调用JTextArea.append()

时间:2018-07-04 16:59:54

标签: java multithreading swing

我正在使用WindowBuilder在Java中创建一些聊天功能。我的GUI类创建了一个线程,我希望从该线程中更新超类的TextArea。我创建线程的原因是我希望能够中断子类内部的代码。

我的问题是我无法从子类的线程中追加到超类的TextArea。

我尝试将代码缩减为基本要点:

import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JMenu;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class SSCCE {

    private JFrame frmRoom;

    private final static String newline = "\r\n";

    private JTextField textField;
    private JScrollPane scrollPane;
    private JTextArea textArea;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        try {
        } catch (Throwable e) {
            e.printStackTrace();
        }
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    SSCCE window = new SSCCE();
                    window.frmRoom.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public SSCCE() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frmRoom = new JFrame();
        frmRoom.setTitle("Test");
        frmRoom.setBounds(100, 100, 450, 300);
        frmRoom.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JMenuBar menuBar = new JMenuBar();
        frmRoom.setJMenuBar(menuBar);

        JMenu mnButton1 = new JMenu("Button 1");
        menuBar.add(mnButton1);

        JMenuItem mntmButton2 = new JMenuItem("Button 2");
        mntmButton2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                Thread t = new Thread(new SSCCESub());
                SwingUtilities.invokeLater(t);
                //t.start(); 
                //Neither of the above work.
            }
        });
        mnButton1.add(mntmButton2);

        textField = new JTextField();

        scrollPane = new JScrollPane();
        textArea = new JTextArea();
        textArea.setEditable(false);
        scrollPane.setViewportView(textArea);

        GroupLayout groupLayout = new GroupLayout(frmRoom.getContentPane());
        groupLayout.setHorizontalGroup(
            groupLayout.createParallelGroup(Alignment.LEADING)
                .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 434, Short.MAX_VALUE)
                .addGroup(Alignment.TRAILING, groupLayout.createSequentialGroup()
                    .addComponent(textField, GroupLayout.PREFERRED_SIZE, 434, Short.MAX_VALUE)
                    .addGap(0))
        );
        groupLayout.setVerticalGroup(
            groupLayout.createParallelGroup(Alignment.LEADING)
                .addGroup(groupLayout.createSequentialGroup()
                    .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 214, Short.MAX_VALUE)
                    .addComponent(textField, GroupLayout.PREFERRED_SIZE, 28, GroupLayout.PREFERRED_SIZE))
        );
        frmRoom.getContentPane().setLayout(groupLayout);

    }

    private void addLineToTextArea(String line) {
        System.out.println("Tried calling a superclass method in order to append to the TextArea");
        textArea.append(line + newline);
    }

    private static class SSCCESub extends SSCCE implements Runnable {

        public void run() {
                super.textArea.append("This won't be visible" + newline); //TODO This is what my question is about.
                super.addLineToTextArea("This won't be visible");
                System.out.println("This should be visible");
                return;
        }
    }
}

1 个答案:

答案 0 :(得分:1)

简单的答案是:不要使用继承来尝试促进子对象与父对象之间的通信。那不是继承的目的,不是继承的工作原理(正如您所发现的)。而是使用组合-使用构造函数或方法参数将object1的实例传递给object2,并调用公共方法传递信息。

因此,您的第二个类可以具有一个采用SSCCE参数的构造函数,允许您传入该参数,然后设置SSCCE字段,这可以使您调用当前SSCCE对象的公共方法。

接下来,修复此问题后,您的代码将运行在Swing线程规则之外-您只应在Swing事件线程上更改Swing组件。请阅读Lesson: Concurrency in Swing

更多详细的答案来了......

例如,假设您想将Swing GUI与套接字挂钩,以进行简单的聊天通信。我们可以创建一个新的GUI类,称为SSCCE2,类似....

json

使用一个公共方法,例如import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.io.IOException; import java.io.PrintWriter; import java.net.Socket; import javax.swing.*; @SuppressWarnings("serial") public class SSCCE2 extends JPanel { private static final int GAP = 4; private Action submitAction = new SubmitAction("Submit"); private JTextField textField = new JTextField(40); private JTextArea textArea = new JTextArea(20, 40); private JButton submitButton = new JButton(submitAction); private PrintWriter printWriter = null; private ChatWorker chatWorker = null; public SSCCE2(Socket socket) throws IOException { printWriter = new PrintWriter(socket.getOutputStream()); chatWorker = new ChatWorker(this, socket); chatWorker.execute(); textArea.setFocusable(false); JScrollPane scrollPane = new JScrollPane(textArea); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); textField.setAction(submitAction); JPanel bottomPanel = new JPanel(); bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.LINE_AXIS)); bottomPanel.add(textField); bottomPanel.add(submitButton); setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP)); setLayout(new BorderLayout(GAP, GAP)); add(scrollPane); add(bottomPanel, BorderLayout.PAGE_END); } // Action (acts as an ActionListener) that gets text from // JTextField and puts it into JTextArea // And also sends it via PrintWriter to the Socket private class SubmitAction extends AbstractAction { public SubmitAction(String name) { super(name); } @Override public void actionPerformed(ActionEvent e) { String text = textField.getText(); textField.selectAll(); // send this to the socket for chatting.... if (printWriter != null) { printWriter.println(text); } textArea.append(text); textArea.append("\n"); } } // public method to allow outside objects to append to the JTextArea public void append(String text) { textArea.append(text); textArea.append("\n"); } } ,该方法允许外部类追加到JTextArea,然后我们将其实例传递到需要的地方,这里:public void append(String text)通过传递{{1} }。然后我们的ChatWorker可以调用公共方法:

chatWorker = new ChatWorker(this, socket);