有。我正在编写一个GUI聊天客户端,该客户端将消息发送到服务器并接收来自服务器的回声。
客户端JFrame的上半部分包含一个JTextArea,该JTextArea包装在JScrollPane中,该JScrollPane置于菜单栏下方。此JScrollPane负责从服务器接收响应,该服务器从客户端接收消息并将其广播给所有客户端。因此,客户端将专用线程用于接收消息。
下半部分包含一个JPanel,它本身包含一个JTextArea和一个JButton。此JPanel负责接收用户输入的消息,并将其发送到服务器。
现在的问题是,当运行客户端JFrame时,上半部分(即带有JTextArea的JScrollPane)不会显示在JFrame的上半部分。另一个问题是,尽管我已将JTextArea设置为可编辑的,但我不能在下半部分的JTextArea中键入任何内容。
[请帮助我破解这两个神话。非常感谢!
客户端代码如下:
package chatroom;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
import java.util.*;
public class MultithreadEchoChatroomClientGUI {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable () {
public void run () {
ClientFrame client = new ClientFrame();
client.setTitle("Chat");
client.setSize(400, 500);
client.setVisible(true);
}
});
}
}
class ClientFrame extends JFrame {
private Socket socket;
private Scanner input;
private PrintWriter output;
private JTextArea serverResponseArea;
private JTextArea messageArea;
private JButton sendButton;
private static final int PORT = 1234;
public ClientFrame () {
initNetwork();
initFrame();
}
private void initNetwork () {
String address;
InetAddress host = null;
address = JOptionPane.showInputDialog("Enter the host name or IP address:");
try {
host = InetAddress.getByName(address);
}
catch (UnknownHostException uhEx) {
JOptionPane.showMessageDialog(null, "Unknown Host!", "Error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
try {
socket = new Socket(host, PORT);
input = new Scanner(socket.getInputStream());
}
catch (IOException ioEx) {
JOptionPane.showMessageDialog(null, ioEx.toString(), "Error", JOptionPane.ERROR_MESSAGE);
}
try {
input = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream(), true);
}
catch (IOException ioEx) {
JOptionPane.showMessageDialog(this, "Cannot create input or output stream!", "Error", JOptionPane.ERROR_MESSAGE);
closeSocket();
System.exit(1);
}
}
private final void closeSocket () {
try {
socket.close();
}
catch (IOException ioEx) {
JOptionPane.showMessageDialog(this, "Cannot disconnect from chatroom!", "Error", JOptionPane.ERROR_MESSAGE);
}
}
private void initFrame () {
JMenuBar menuBar = createMenuBar();
setJMenuBar(menuBar);
JScrollPane responsePanel = createResponsePanel();
add(responsePanel, BorderLayout.NORTH);
JPanel messagePanel = createMessagePanel();
add(messagePanel, BorderLayout.CENTER);
addWindowListener(new WindowAdapter () {
@Override
public void windowClosing (WindowEvent we) {
closeSocket();
System.exit(0);
}
});
new Thread(new Runnable () {
public void run () {
String clientName = null;
String serverResponse = null;
Scanner in = null;
PrintWriter out = null;
try {
in = new Scanner(socket.getInputStream());
out = new PrintWriter(socket.getOutputStream());
}
catch (IOException ioEx) {
JOptionPane.showMessageDialog(ClientFrame.this, "Cannot create input or output stream!",
"Error", JOptionPane.ERROR_MESSAGE);
closeSocket();
System.exit(1);
}
do {
clientName = JOptionPane.showInputDialog("What nickname would you like to use in the chatroom?");
} while (clientName == null);
out.println("#" + clientName);
serverResponse = in.nextLine();
serverResponseArea.append(serverResponse);
}
}).start();
}
private JMenuBar createMenuBar () {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Operations");
JMenuItem quit = new JMenuItem("Quit");
quit.addActionListener(new ActionListener() {
@Override
public void actionPerformed (ActionEvent event) {
closeSocket();
System.exit(0);
}
});
menu.add(quit);
menuBar.add(menu);
return menuBar;
}
private JScrollPane createResponsePanel () {
JScrollPane scrlPane = new JScrollPane();
scrlPane.setBorder(BorderFactory.createEmptyBorder(20, 10, 10, 20));
serverResponseArea = new JTextArea(30, 50);
serverResponseArea.setEditable(false);
serverResponseArea.setLineWrap(true);
serverResponseArea.setWrapStyleWord(true);
scrlPane.add(serverResponseArea);
return scrlPane;
}
private JPanel createMessagePanel () {
JPanel msgPanel = new JPanel();
msgPanel.setBorder(BorderFactory.createEmptyBorder(20,10, 10, 20));
msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.LINE_AXIS));
JScrollPane srlPanel = createMessageTextPanel();
msgPanel.add(srlPanel);
JButton sdButton = createSendButton();
msgPanel.add(sdButton);
return msgPanel;
}
private JScrollPane createMessageTextPanel () {
JScrollPane mtPanel = new JScrollPane();
messageArea = new JTextArea(15, 35);
messageArea.setEditable(true);
messageArea.setLineWrap(true);
messageArea.setWrapStyleWord(true);
mtPanel.add(messageArea);
return mtPanel;
}
private JButton createSendButton () {
JButton button = new JButton("Send");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed (ActionEvent event) {
String message;
message = messageArea.getText();
output.println(message);
messageArea.setText("");
}
});
return button;
}
}
由于@MadProgrammer提供的线索,我发现问题出在我将JTextArea添加到JScrollPane的方式中。它不应该通过scrlPane.add(serverResponseArea);
完成,而应该通过JScrollPane scrlPane = new JScrollPane(serverResponseArea);
完成。
但是,由于scrlPane.add(serverResponseArea);
并不能解决问题,也不产生任何明显的视觉效果,为什么编译器首先让它脱颖而出,而运行时没有抛出此类异常?这不是库设计中的错误吗? add
方法可能是从父组件继承的,但是如果对子组件“无用”,为什么不将其踢出子组件呢?也许由于某种原因该方法继续停留在子组件中,但可能会导致问题。