0

I have been working on this problem for a couple of days and I cannot understand where I went wrong. I have created a Server-Client Chat Program and on the Server GUI there is a tab that shows a list of users. This list works in every way that I want. I wanted to add a UserList to the Client GUI and the JList is there, but when I update the DefaultListModel, the JList only updates on the ServerGUI. I tried to debug and found that the JList on the ChatGUI is not displayable and I don't know why, or how to fix it.

Here is my (relevant) code:

Client Class

public class Client {
    String username;
    Socket socket;
    PrintWriter out;
    Scanner in;

    public Client (String username, Socket socket, PrintWriter out, Scanner in) {
        this.username = username;
        this.socket = socket;
        this.out = out;
        this.in = in;
    }
}

ServerGUI Class - (the register method is called later in the program when a Client joins)

public class ServerGUI {
    public volatile static ArrayList<Client> users;
    public static DefaultListModel<String> model = new DefaultListModel<String>();
    static Client clientReg;

    public static void register(Client client) {
        clientReg = client;
        users.add(clientReg);
        model.addElement(clientReg.username);
         ServerView.userList.setModel(model);
        ChatView.userList.setModel(model);
    }
}

ChatView Class

public class ChatView extends JFrame {
    public JPanel contentPane;
    public static JList<String> userList = new JList<String>();
    public static JTextArea chatOutput;
    private JTextField inputField;

    public ChatView() {
        setResizable(false);
        setTitle("Chat GUI");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 550, 475);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);

        JTabbedPane tabs = new JTabbedPane(JTabbedPane.TOP);
        tabs.setBounds(0, 0, 535, 435);
        contentPane.add(tabs);

        JPanel chatViewer = new JPanel();
        tabs.addTab("Chat", null, chatViewer, null);
        chatViewer.setLayout(null);

        // Code that makes up the chatViewer JPanel

        JPanel userListPane = new JPanel();
        tabs.addTab("User List", null, userListPane, null);
        userListPane.setLayout(null);

        JLabel label = new JLabel("User List:");
        label.setBounds(10, 10, 510, 20);
        userListPane.add(label);

        userList.setModel(new AbstractListModel<String>() {
            public String getElementAt(int index) {
                return ServerGUI.model.get(index);
            }

            public int getSize() {
                return ServerGUI.model.size();
            }
        });
        userList.setValueIsAdjusting(true);
        userList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
        userList.setBounds(10, 40, 510, 395);

        userListPane.add(userList);
    }
}

Most of my programming has been self taught so if there is any format that is incorrect please let me know so that I may be able to correct it.

Frakcool
  • 10,915
  • 9
  • 50
  • 89
bh34e5
  • 21
  • 4
  • 1
    Don't use null layouts. If you've read any Swing Q/A on this site, you'll already know that this is not recommended and for very good reasons. – Hovercraft Full Of Eels Dec 13 '17 at 20:05
  • userListPane was added to a different tab. I accidentally forgot to add that to the code that I selected – bh34e5 Dec 13 '17 at 20:09
  • 1
    So does the JList work now? Can you now see it in the tab? But again I reiterate, don't use null layouts. Instead set the JList's visible row count and give it a prototype element so that it set its own displayed size within a JScrollPane since JLists should always be displayed within JScrollPanes. Get rid of setBounds everywhere and learn to use the layout managers. – Hovercraft Full Of Eels Dec 13 '17 at 20:12
  • I know that null layouts aren't the best but it has been working for this project and the JList has always been visible, its just that when I update the model, the JList doesn't update with it. I will work on adding it to a scroll pane and let you know if that fixes it – bh34e5 Dec 13 '17 at 20:14
  • 1
    You're working against the Swing library when you do this. Have you tried to maintain a complex program that uses null layouts? Having to manually shift everything? Getting complaints from people who use other platforms stating that your labels are not readable? Best to work with library not against it. OK, so your problem is not that the JList doesn't display but that it doesn't update? Is this a Swing threading problem? You may not have posted enough code to answer this, and may need to post a valid [mcve] (please check the link). – Hovercraft Full Of Eels Dec 13 '17 at 20:17
  • The White background of the JList is visible. I tried to debug and used the method `isDisplayable` and it returned false. I don't why the JList is not updating with the DefaultListModel because I have another JList on another GUI that does update. Is it because I am trying to use the same model for two different JLists? Does it have anything to do with the fact that I am trying to update both of the JLists right after the other? I am using multiple threads but when I tried to surround the `setModel` method in an InvokeLater, it still didn't work – bh34e5 Dec 13 '17 at 20:30
  • OK, you're trying to "share" data using static fields, and that won't and can't work. Delete it and instead pass information from server to client through your socket connection. – Hovercraft Full Of Eels Dec 13 '17 at 20:35

1 Answers1

2

OK, I see how you're trying to "share" models via static fields and methods:

userList.setModel(new AbstractListModel<String>() {
    public String getElementAt(int index) {
        return ServerGUI.model.get(index);
    }

    public int getSize() {
        return ServerGUI.model.size();
    }
});

and this will never work, and is not how clients and servers transfer information. For one, while yes you have the server static fields and methods available to the client, the actual server class and instance are running in a completely different JVM, usually on a different machine entirely, and so the data that you're getting does not reflect the true state of the actual Server object and static class state on the server itself, but is a mere shadow of the correct object/class.

Suggestions:

  • Use a standard list model for your client, and delete the code you're using, it won't work
  • If this is true client/server code where you transmit information over a socket, then the client must get updates from the server through the socket. We have no idea how you're wiring this so, I cannot give specific recommendations other then -- get the data from the socket
  • And do so in a background thread -- i.e., the client should receive socket data off of the Swing event thread
  • But then use this information received to update the client's list model on the event thread. One way to do this is via a SwingWorker<Void, String> using its process / publish method pair.
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • I just want to clarify: you are recommending that I use `ListModel` instead of `DefaultListModel`. As I am fairly new to programming I'm not completely sure how to know if I am off of the Swing Event thread so how would I know that I am. – bh34e5 Dec 13 '17 at 22:06
  • @bh34e5: no, not at all. Use a class that derives from ListModel which can include DefaultListModel. – Hovercraft Full Of Eels Dec 14 '17 at 11:45