Search code examples
javaswinguser-interfacejtextarea

Updating UI for JTextArea in Java


I'm a beginner and I've hit a bump in the road.

The problem I have is that my JTextArea is not updating as my program is running. I ran a command line to see whether the value of the JTextArea is changing. It turns out that it is but on the UI it remains the same until the end, then it shows the final change that I had made. Could you please help me find a solution to this problem?

Here follows the code where the problem arises. (In my ActionListener.)

private class ButtonHandler implements ActionListener   //Start of inner class for event handling of buttons
{
        public void actionPerformed( ActionEvent event )
        {//start method that prosesses the events for the buttons

            if(event.getSource() == call)
            {
                do {
                    delay.myDelay(2000); //simply a Thread.sleep(I intend to repeat this a lot so I made a class for it.

                    elevatorFloor = eCall.calling();
                    eCall.setFloor(elevatorFloor);
                    System.out.println("getfloor in call= " +eCall.getFloor()); //Test yields correct value

                    eFloor = ind.level(eCall.getFloor()); //String that gives floor number
                    floorIndicator.setText(eFloor); //DOES NOT CHANGE IN GUI

                    String value = floorIndicator.getText();
                    System.out.println("Floorindicator is= " +value);//Test yields that the value  is changing dynamically in command line

                    floorIndicator.updateUI();  //Stuff I found on the internet that didnt work
                    floorIndicator.revalidate();
                    floorIndicator.validate();


                } while(eCall.getFloor() != eCall.getUserFloor());

                    delay.myDelay(2000);

                    doorImage.setIcon(door1);
                }
           }
      }

Solution

  • In Java, the UI (drawing of components, events, etc) is handled by a special thread called the Event Dispatching Thread. Since this thread determines how quickly will your UI respond to events, it is important that you do only UI related operations on it.

    Doing operations which might be blocking, such as Thread.sleep are not recommended to be done in this thread since it blocks the execution of the thread and thus makes your application not respond to events in a timely manner. This is why in some of the comments, you where told that you are blocking the EDT.

    In you case, looping can also be avoided since with each iteration, you are waiting for a period of two seconds (I am assuming to give the impression of the escalator moving between floors).

    The solution to this, (one of them at least), would be to place the entire body of the do...while loop in a separate thread. This thread will be then launched by the event handler. This solution would not block the event dispatching thread, but now there is another problem: Only the EDT can upload the UI, thus, the approach above on its own would not change the text.

    For this purpose, Java provides a SwingWorker class which allows code to queue items on the EDT.

    Thus in short, your code would like something like so:

    private class ButtonHandler implements ActionListener   //Start of inner class for event handling of buttons
    {
            public void actionPerformed( ActionEvent event )
            {//start method that prosesses the events for the buttons
    
                if(event.getSource() == call)
                {
                    //final int elFloor = eCall.calling(); i ran it without this and it worked
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            do{
                            /*There is nothing wrong with calling Thread.sleep directly
                             *wrapping a one liner with your own implementation might cause
                             *more confusion
                             */                     
                            delay.myDelay(2000);
    
                            //Not sure what is elevatorFloor
                            elevatorFloor = eCall.calling();
                            eCall.setFloor(elevatorFloor);
                            System.out.println("getfloor in call= " +eCall.getFloor()); //Test yields correct value
    
                            eFloor = ind.level(eCall.getFloor()); //String that gives floor number
                            SwingUtilities.invokeLater(new Runnable(){
                                @Override
                                public void run() {
                                    floorIndicator.setText(eFloor); //DOES NOT CHANGE IN GUI
                                }
                            });
    
    
                            String value = floorIndicator.getText();
                            System.out.println("Floorindicator is= " +value);//Test yields that the value  is changing dynamically in command line
    
                            }while(eCall.getFloor() != eCall.getUserFloor());
    
                            //delay.myDelay(2000);
    
                            doorImage.setIcon(door1);
                        }
                    }).start();
                }
           }
    }
    

    Note: The code above has not been tested and you might need to make additional changes, especially on how you can access the variables from within the thread scopes, but it should provide an overview of what you need to do.