此代码在 window 控制台上显示代码,但不在我使用的 textArea 上显示代码
This code is displaying code on window console but not on the textArea which I had used
下面是我的代码:
package Project1;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.Font;
import javax.swing.JScrollBar;
import java.awt.Color;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class DonorChat extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
String get=null;
String s1=null;
DataOutputStream dos;
DataInputStream dis;
JButton btnNewButton;
private JPanel contentPane;
public JTextField textField;
public JTextArea textArea ;
public JButton btnNewButton_1;
public DonorChat() {
setTitle("Donor Chat");
// setIconImage(Toolkit.getDefaultToolkit().getImage("E:\algorithm\Project1\Project1\Blood.png"));
setIconImage(Toolkit.getDefaultToolkit().getImage("E:\algorithm\Project1\Project1\Blood.png"));
setForeground(Color.RED);
setFont(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(0, 0, 800, 1000);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
textArea= new JTextArea();
textArea.setFont(new Font("Tahoma", Font.BOLD, 17));
textArea.setBounds(31, 222, 707, 522);
contentPane.add(textArea);
textField = new JTextField();
textField.setFont(new Font("Tahoma", Font.BOLD, 17));
textField.setBounds(31, 793, 510, 105);
contentPane.add(textField);
textField.setColumns(10);
textField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
});
btnNewButton = new JButton("Send\r\n");
btnNewButton.setFont(new Font("Tahoma", Font.BOLD, 17));
btnNewButton.setForeground(Color.RED);
btnNewButton.setBackground(Color.LIGHT_GRAY);
btnNewButton.setBounds(602, 820, 125, 47);
contentPane.add(btnNewButton);
JScrollBar scrollBar = new JScrollBar();
scrollBar.setBounds(717, 222, 21, 522);
contentPane.add(scrollBar);
btnNewButton_1 = new JButton("CONNECT TO SERVER");
btnNewButton_1.setBackground(Color.RED);
btnNewButton_1.setForeground(Color.DARK_GRAY);
btnNewButton_1.setFont(new Font("Times New Roman", Font.BOLD, 29));
btnNewButton_1.setBounds(31, 60, 707, 86);
contentPane.add(btnNewButton_1);
btnNewButton_1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
try {
ServerSocket ss=new ServerSocket(9995);
Socket snSocket=ss.accept();
dos=new DataOutputStream(snSocket.getOutputStream());
dis=new DataInputStream(snSocket.getInputStream());
getValue();
ss.close();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
setVisible(true);
}
public void getValue() throws IOException{
btnNewButton.removeActionListener(null);
while(true){
s1=dis.readUTF();
if (s1.equals("stop")){
textArea.setText("Client Want to Stop:"+s1);
break;
}
else{
System.out.println("Client Says:"+s1);
textArea.setText("Client Says:"+s1);
}
System.out.println("Type Something for Client");
btnNewButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0){
try {
dos.writeUTF(get);
}
catch(Exception e){
}
}
});
}
}
}
我正在为服务器构建一个 window,它需要一个连接按钮来连接服务器套接字,但是在为该服务器设置客户端并向客户端发送消息后,该消息显示在eclipse 的控制台,但不在我真正想要的 textarea 上
所以请帮我看看我的代码。
两个问题:
- 您的
while (true)
循环正在阻塞 AWT 事件分派线程。
- 您在循环的每次迭代中都添加了一个侦听器。
阻塞事件队列
AWT/Swing is single threaded。当调用“CONNECT TO SERVER”按钮的 ActionListener 时,它会在 AWT 事件调度线程中调用。在该方法 returns.
之前,不会处理其他事件
因此,当 ActionListener 调用 getValue()
并且 getValue() 从套接字读取数据直到遇到 "stop"
时,所有事件处理都会暂停。什么都不会重新粉刷。不会有鼠标或键盘输入的响应,因为没有处理MouseEvents和KeyEvents。
您必须执行 I/O 操作,例如从不同线程上的套接字读取数据。但是,Swing 方法必须在 AWT 事件调度线程中执行。
一种解决方案是使用 SwingWorker class,其 publish
和 process
方法允许您将多个数据元素从后台线程发送到 AWT,甚至分派线程。
为了发送到套接字,您可以使用线程安全的 BlockingQueue 来存储要发送的行,然后在不同的线程(而不是 AWT 事件调度线程)中使用循环从该 BlockingQueue 中获取文本并将其发送到套接字。
只添加一次监听器
方法 addActionListener
实际上 添加一个监听器 到一个按钮。 您添加的所有 个侦听器将在按下按钮时被调用。
因此,如果您在每次循环执行时都调用 addActionListener
,那么您将为从套接字读取的每条数据添加一个侦听器!
您应该只将 ActionListener 添加到按钮一次 — 通常是在您创建按钮之后。
代码
因此,如果我们将 I/O 移动到其他线程并使用 BlockingQueue 来跟踪要发送的行,它看起来像这样:
private final BlockingQueue<String> linesToSend =
new LinkedBlockingDeque<>();
public DonorChat() {
// ...
textField.addActionListener(new ActionListener() {
linesToSend.add(textField.getText());
});
btnNewButton.addActionListener(new ActionListener() {
linesToSend.add(textField.getText());
});
// ...
}
private void getValue(Socket snSocket) {
textArea.setText("");
SwingWorker<Void, String> socketReader =
new SwingWorker<Void, String>() {
@Override
protected Void doInBackground()
throws IOException {
// Runs in another thread.
// No AWT/Swing calls allowed here!
dis = new DataInputStream(snSocket.getInputStream());
while (true) {
String s1 = dis.readUTF();
if (s1.equals("stop")) {
publish("Client wants to stop.");
break;
}
publish("Client says: " + s1);
}
snSocket.close();
return null;
}
@Override
protected void process(List<String> linesRead) {
// Runs in event dispatch thread thread.
// AWT/Swing calls only; no I/O allowed here!
for (String line : linesRead) {
textArea.append(line + "\n");
}
}
};
socketReader.execute();
// Lines to send were added in AWT event dispatch thread
// by ActionListeners.
// We want to send them to the socket in a different thread.
Runnable linesSender = new Runnable() {
@Override
public void run() {
try {
dos = new DataOutputStream(snSocket.getOutputStream());
while (true) {
dos.writeUTF(linesToSend.take());
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(linesSender).start();
}
其他一些重要说明:
- 永远不要写一个空的
catch
块。异常会告诉你哪里出了问题,这是非常有价值的信息。如果您不确定要在 catch
块中放入什么,请写入 e.printStackTrace();
以便您可以查看异常的完整详细信息。
- Learn to use layout managers. 空布局在您的计算机上看起来没问题,但在其他计算机上就会出现问题,因为安装的字体不同并且字体以不同的大小呈现。 (“17 点”表示 ¹⁷⁄₇₂ 英寸,在 120 DPI 显示器上呈现的像素比在 96 DPI 显示器上呈现的像素更多。)此外,空布局意味着您无法调整 window 的大小来制作 JTextArea 和JTextField 更大或更小。
下面是我的代码:
package Project1;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.Font;
import javax.swing.JScrollBar;
import java.awt.Color;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class DonorChat extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
String get=null;
String s1=null;
DataOutputStream dos;
DataInputStream dis;
JButton btnNewButton;
private JPanel contentPane;
public JTextField textField;
public JTextArea textArea ;
public JButton btnNewButton_1;
public DonorChat() {
setTitle("Donor Chat");
// setIconImage(Toolkit.getDefaultToolkit().getImage("E:\algorithm\Project1\Project1\Blood.png"));
setIconImage(Toolkit.getDefaultToolkit().getImage("E:\algorithm\Project1\Project1\Blood.png"));
setForeground(Color.RED);
setFont(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(0, 0, 800, 1000);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
textArea= new JTextArea();
textArea.setFont(new Font("Tahoma", Font.BOLD, 17));
textArea.setBounds(31, 222, 707, 522);
contentPane.add(textArea);
textField = new JTextField();
textField.setFont(new Font("Tahoma", Font.BOLD, 17));
textField.setBounds(31, 793, 510, 105);
contentPane.add(textField);
textField.setColumns(10);
textField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
});
btnNewButton = new JButton("Send\r\n");
btnNewButton.setFont(new Font("Tahoma", Font.BOLD, 17));
btnNewButton.setForeground(Color.RED);
btnNewButton.setBackground(Color.LIGHT_GRAY);
btnNewButton.setBounds(602, 820, 125, 47);
contentPane.add(btnNewButton);
JScrollBar scrollBar = new JScrollBar();
scrollBar.setBounds(717, 222, 21, 522);
contentPane.add(scrollBar);
btnNewButton_1 = new JButton("CONNECT TO SERVER");
btnNewButton_1.setBackground(Color.RED);
btnNewButton_1.setForeground(Color.DARK_GRAY);
btnNewButton_1.setFont(new Font("Times New Roman", Font.BOLD, 29));
btnNewButton_1.setBounds(31, 60, 707, 86);
contentPane.add(btnNewButton_1);
btnNewButton_1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
try {
ServerSocket ss=new ServerSocket(9995);
Socket snSocket=ss.accept();
dos=new DataOutputStream(snSocket.getOutputStream());
dis=new DataInputStream(snSocket.getInputStream());
getValue();
ss.close();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
setVisible(true);
}
public void getValue() throws IOException{
btnNewButton.removeActionListener(null);
while(true){
s1=dis.readUTF();
if (s1.equals("stop")){
textArea.setText("Client Want to Stop:"+s1);
break;
}
else{
System.out.println("Client Says:"+s1);
textArea.setText("Client Says:"+s1);
}
System.out.println("Type Something for Client");
btnNewButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0){
try {
dos.writeUTF(get);
}
catch(Exception e){
}
}
});
}
}
}
我正在为服务器构建一个 window,它需要一个连接按钮来连接服务器套接字,但是在为该服务器设置客户端并向客户端发送消息后,该消息显示在eclipse 的控制台,但不在我真正想要的 textarea 上
所以请帮我看看我的代码。
两个问题:
- 您的
while (true)
循环正在阻塞 AWT 事件分派线程。 - 您在循环的每次迭代中都添加了一个侦听器。
阻塞事件队列
AWT/Swing is single threaded。当调用“CONNECT TO SERVER”按钮的 ActionListener 时,它会在 AWT 事件调度线程中调用。在该方法 returns.
之前,不会处理其他事件因此,当 ActionListener 调用 getValue()
并且 getValue() 从套接字读取数据直到遇到 "stop"
时,所有事件处理都会暂停。什么都不会重新粉刷。不会有鼠标或键盘输入的响应,因为没有处理MouseEvents和KeyEvents。
您必须执行 I/O 操作,例如从不同线程上的套接字读取数据。但是,Swing 方法必须在 AWT 事件调度线程中执行。
一种解决方案是使用 SwingWorker class,其 publish
和 process
方法允许您将多个数据元素从后台线程发送到 AWT,甚至分派线程。
为了发送到套接字,您可以使用线程安全的 BlockingQueue 来存储要发送的行,然后在不同的线程(而不是 AWT 事件调度线程)中使用循环从该 BlockingQueue 中获取文本并将其发送到套接字。
只添加一次监听器
方法 addActionListener
实际上 添加一个监听器 到一个按钮。 您添加的所有 个侦听器将在按下按钮时被调用。
因此,如果您在每次循环执行时都调用 addActionListener
,那么您将为从套接字读取的每条数据添加一个侦听器!
您应该只将 ActionListener 添加到按钮一次 — 通常是在您创建按钮之后。
代码
因此,如果我们将 I/O 移动到其他线程并使用 BlockingQueue 来跟踪要发送的行,它看起来像这样:
private final BlockingQueue<String> linesToSend =
new LinkedBlockingDeque<>();
public DonorChat() {
// ...
textField.addActionListener(new ActionListener() {
linesToSend.add(textField.getText());
});
btnNewButton.addActionListener(new ActionListener() {
linesToSend.add(textField.getText());
});
// ...
}
private void getValue(Socket snSocket) {
textArea.setText("");
SwingWorker<Void, String> socketReader =
new SwingWorker<Void, String>() {
@Override
protected Void doInBackground()
throws IOException {
// Runs in another thread.
// No AWT/Swing calls allowed here!
dis = new DataInputStream(snSocket.getInputStream());
while (true) {
String s1 = dis.readUTF();
if (s1.equals("stop")) {
publish("Client wants to stop.");
break;
}
publish("Client says: " + s1);
}
snSocket.close();
return null;
}
@Override
protected void process(List<String> linesRead) {
// Runs in event dispatch thread thread.
// AWT/Swing calls only; no I/O allowed here!
for (String line : linesRead) {
textArea.append(line + "\n");
}
}
};
socketReader.execute();
// Lines to send were added in AWT event dispatch thread
// by ActionListeners.
// We want to send them to the socket in a different thread.
Runnable linesSender = new Runnable() {
@Override
public void run() {
try {
dos = new DataOutputStream(snSocket.getOutputStream());
while (true) {
dos.writeUTF(linesToSend.take());
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(linesSender).start();
}
其他一些重要说明:
- 永远不要写一个空的
catch
块。异常会告诉你哪里出了问题,这是非常有价值的信息。如果您不确定要在catch
块中放入什么,请写入e.printStackTrace();
以便您可以查看异常的完整详细信息。 - Learn to use layout managers. 空布局在您的计算机上看起来没问题,但在其他计算机上就会出现问题,因为安装的字体不同并且字体以不同的大小呈现。 (“17 点”表示 ¹⁷⁄₇₂ 英寸,在 120 DPI 显示器上呈现的像素比在 96 DPI 显示器上呈现的像素更多。)此外,空布局意味着您无法调整 window 的大小来制作 JTextArea 和JTextField 更大或更小。