gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 11:03:12 24 4
gpt4 key购买 nike

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

我的问题是我无法从子类的线程内附加到父类(super class)的 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;
}
}
}

最佳答案

简短的回答是:不要使用继承来尝试促进子对象和父对象之间的通信。这不是继承的目的,也不是它的工作原理(正如您所发现的)。而是使用组合——使用构造函数或方法参数将 object1 的实例传递到 object2 中,并调用公共(public)方法来传递信息。

因此,您的第二个类可以有一个接受 SSCCE 参数的构造函数,允许您将其传入,然后设置一个 SSCCE 字段,这可以允许您调用当前 SSCCE 对象的公共(public)方法。

接下来,一旦解决了这个问题,您的代码运行就会违反 Swing 线程规则——您应该只在 Swing 事件线程上改变 Swing 组件。请阅读Lesson: Concurrency in Swing

更详细的答案即将到来......

例如,假设您想将 Swing GUI 与 Socket Hook 以进行简单的聊天通信。我们可以创建一个新的 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)方法,例如 public void append(String text) ,它允许外部类附加到 JTextArea,然后我们将其实例传递到需要的地方,此处: chatWorker = new ChatWorker(this, socket); 通过传入this。然后我们的ChatWorker就可以调用公共(public)方法了:

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
}
}
}

关于java - 从子类的线程调用 JTextArea.append(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51178253/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com