Вызов JTextArea.append () из потока подкласса - PullRequest
0 голосов
/ 04 июля 2018

Я создаю некоторые функции чата в Java, используя WindowBuilder. Мой класс 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 голосов
/ 04 июля 2018

Короткий ответ: не используйте наследование, чтобы попытаться облегчить общение между дочерними и родительскими объектами. Это не то, для чего предназначено наследование, и не как оно работает (как вы выяснили). Вместо этого используйте состав - передайте экземпляр object1 в object2, используя параметры конструктора или метода, и вызовите открытые методы для передачи информации.

Таким образом, ваш 2-й класс может иметь конструктор, который принимает параметр SSCCE, позволяет вам передать его, затем установить поле SSCCE, и это может позволить вам вызывать открытые методы вашего текущего объекта SSCCE.

Далее, как только вы исправите это, ваш код будет нарушать правила потоков Swing - вы должны только мутировать компоненты Swing в потоке событий Swing. Пожалуйста, прочитайте Урок: параллелизм в Swing

Более подробный ответ идет ....

Например, скажем, вы хотите подключить Swing GUI к Socket для простого общения в чате. Мы могли бы создать новый класс GUI, скажем, под названием SSCCE2, что-то вроде ....

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

}

с открытым методом, скажем, public void append(String text), который позволяет внешним классам присоединяться к JTextArea, тогда мы передадим экземпляр этого в нужное место, здесь: chatWorker = new ChatWorker(this, socket);, передав this. Тогда наш ChatWorker может вызывать публичные методы:

import java.io.IOException;
import java.net.Socket;
import java.util.List;
import java.util.Scanner;
import javax.swing.SwingWorker;

//better if the threading is done with a SwingWorker 
//to not run afoul of Swing threading rules
public class ChatWorker extends SwingWorker<Void, String> {
    private SSCCE2 sscce2 = null;
    private Scanner scanner = null;

    public ChatWorker(SSCCE2 sscce2, Socket socket) throws IOException {
        this.sscce2 = sscce2; // get the instance and assign to field
        scanner = new Scanner(socket.getInputStream());
    }

    @Override
    protected Void doInBackground() throws Exception {
        // this is called in a background thread
        while (scanner.hasNextLine()) {
            publish(scanner.nextLine());
        }
        return null;
    }

    @Override
    protected void process(List<String> chunks) {
        // this is called on the Swing event thread
        for (String text : chunks) {
            sscce2.append(text); // append the texts as they come in
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...