Reputation: 135
Hi I'm small a little issue trying to append some text to a JTextArea
from another class within the same package. Below is the main class that pertains to the JFrame:
public class Client extends JFrame{
//Static variables
private static final String host = "localhost";
private static final int portNumber = 4444;
//Variables
private String userName;
//JFrame Variables
private JPanel contentPanel;
private JTabbedPane panel_Social;
private JPanel jpanel_Social;
private JPanel jpanel_Chat;
private JTextArea textArea_Receive;
private JTextField textField_Send;
private JTextArea textArea_ClientList;
private JButton btn_Enter;
public JTextArea getTextArea_Receive(){
return this.textArea_Receive;
}
//Constructor
private Client(String userName, String host, int portNumber){
this.userName = userName;
this.serverHost = host;
this.serverPort = portNumber;
}
public void main(String args[]){
//Requests user to enter name
String readName = null;
Scanner scan = new Scanner(System.in);
System.out.println("Please enter username");
readName = scan.nextLine();
//Start client
Client client = new Client(readName, host, portNumber);
client.startClient(scan);
}
private void startClient(Scanner scan){
try{
//Create new socket and wait for network communication
Socket socket = new Socket(serverHost, serverPort);
Thread.sleep(1000);
//Create thread and start it
serverThread = new ServerThread(socket, userName);
Thread serverAccessThread = new Thread(serverThread);
serverAccessThread.start();
}
}
}
Below is the serverThread class
public class ServerThread implements Runnable{
private Socket socket;
private String userName;
private boolean isAlived;
private final LinkedList<String> messagesToSend;
private boolean hasMessages = false;
//Constructor
ServerThread(Socket socket, String userName){
this.socket = socket;
this.userName = userName;
messagesToSend = new LinkedList<String>();
}
public void run(){
try{
Client test1 = new Client();
JTextArea test2 = test1.getTextArea_Receive();
String test3 = "Hello World";
test2.append(test3);
} catch (IOException e){
}
}
I included test variables just to easily recreate the issue but whenever the append function is run nothing appears in the text area of the jFrame. In my scenario I'm having the client receive text from a server then append it to the text box.
BTW I'm using the IntelliJ GUI designer for the JFrame. I've only included code needed to recreate the problem. I'm still trying to create MCVE questions so feel free to let me know mistakes I made.
Upvotes: 0
Views: 122
Reputation: 347332
the append function is run nothing appears in the text area of the jFrame
There's not enough information available in you question to ascertain why this might be happening, how ever, there are a number of important pieces of information you need to take into account.
Basically, the first point means that you shouldn't be running any long running or blocking processes within the Event Dispatching Thread AND you should not be modifying the UI (or any state the UI relies on) from outside the context of the Event Dispatching Thread
Start by taking a look at Concurrency in Swing for more details.
The second point means that, for even given part of your code, you want to be asking, "how hard would it be to replace it with some other implementation?" - If the amount of work scares you, then your code is probably to tightly coupled.
To that end, I started with...
public interface Client {
public void append(String message);
}
This is really basic, but it means that some component can send a message to some other component and neither should care about each other beyond this capability.
Next, I looked at ServerThread
. Basically this class becomes responsible for the management of the Socket
and delivery of the messages to the Client
. Because of the requirements of Swing, I've used a SwingWorker
. This allows me to run the Socket
code on a background thread, but ensure that the messages are delivered to the Client
within the context of the Event Dispatching Thread
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.List;
import javax.swing.SwingWorker;
public class ServerThread extends SwingWorker<Void, String> {
private String host;
private int port;
private Client client;
//Constructor
ServerThread(String host, int port, Client client) {
this.host = host;
this.port = port;
this.client = client;
}
@Override
protected void process(List<String> chunks) {
for (String message : chunks) {
client.append(message);
}
}
@Override
protected Void doInBackground() throws Exception {
try (Socket socket = new Socket(host, port)) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
String text = null;
while ((text = reader.readLine()) != null) {
System.out.println(text);
if (text.equals("<stop>")) {
break;
}
publish(text);
}
}
} catch (IOException exp) {
exp.printStackTrace();
publish("Failed to establish connection to " + host + ":" + port + " - " + exp.getMessage());
}
return null;
}
}
And then the actual UI client itself...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Main {
public static void main(String[] args) {
new Main();
}
//Constructor
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
ClientUI clientUI = new ClientUI();
ServerThread thread = new ServerThread("localhost", 4321, clientUI);
thread.execute();
JFrame frame = new JFrame("Client");
frame.add(clientUI);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ClientUI extends JPanel implements Client {
private JTextArea ta;
public ClientUI() {
setLayout(new BorderLayout());
ta = new JTextArea(10, 20);
add(new JScrollPane(ta));
}
@Override
public void append(String message) {
ta.append(message + "\n");
}
}
}
Not much going on here
Finally, I wrote a simple Server
test the code, which simply sends the current date/time as a String
to the connected client.
When I say simple, I mean simple. This is intended, by design, to tell with a single client connection and is meant only for testing the above code
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DateFormat;
import java.util.Date;
public class Server {
public static void main(String[] args) {
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
try (ServerSocket server = new ServerSocket(4321)) {
Socket socket = server.accept();
System.out.println("Connected");
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
while (true) {
System.out.println("Write >> ");
writer.write(format.format(new Date()));
writer.newLine();
writer.flush();
Thread.sleep(1000);
}
}
} catch (IOException | InterruptedException exp) {
exp.printStackTrace();
}
}
}
To test it all, start the Server
and then run the Main
class
Upvotes: 1
Reputation: 755
You should pass Client
into ServerThread
via the constructor. The Client
you are instantiating within run()
is not the same reference to the Client
you created in main()
. So your ServerThread
class would be something like
ServerThread(Client client, Socket socket, String userName) {
this.client = client;
this.socket = socket;
this.userName = userName;
messagesToSend = new LinkedList<String>();
}
public void run() {
try
{
JTextArea test2 = this.client.getTextArea_Receive();
String test3 = "Hello World";
test2.append(test3);
}
catch (IOException e)
{}
}
Your startClient()
method would be updated to something like this
private void startClient(Client client, Scanner scan)
{
try
{
//Create new socket and wait for network communication
Socket socket = new Socket(serverHost, serverPort);
Thread.sleep(1000);
//Create thread and start it
ServerThread serverThread = new ServerThread(client, socket, userName);
serverAccessThread.run();
}
}
All that being said,
I would recommend moving your main()
out of Client
and into a class that isn't so coupled to the Swing UI code. Something like this:
public class MySwingApplication {
private static final String host = "localhost";
private static final int portNumber = 4444;
public static void main(String[] args) {
// Requests user to enter name
// Start client
}
}
Your Client
is then built more like an instance object
public class Client extends JFrame {
public JTextArea getTextArea_Receive(){
// Return the text area
}
// Constructor -- public to allow instantiation from main()
public Client(String userName, String host, int portNumber) {
// Do stuff
}
private void startClient(Scanner scan) {
// Show the JFrame on screen
// Spawn Server
}
}
Upvotes: 1