javaswingevent-dispatch-thread

How to update UI when using Thread.sleep


When running my BubbleSort method I want a panel to update showing exactly what is happening, however the panel freezes until the method has fniished when using Thread.sleep.

I've heard about using Swing timers and I know how to use them but do not know how I could implement them 'mid-method' to pause and continue.

import java.awt.Color;
import java.awt.Font;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;

@SuppressWarnings("serial")
public class BubbleSort extends JPanel implements ActionListener
{  
    int swaps;
    int maxswaps;
    int[] array;
    JLabel arrayLabel;
    JLabel tipLabel = new JLabel();;

    private void bubbleSort(int[] array)
    {  
        maxswaps = (int) Math.pow(array.length,2);
        int tempnum = 0;  
        for(int i=0; i < array.length; i++)
        {  
            for(int num=1; num < (array.length-i); num++)
            {  
                if(array[num-1] > array[num])
                {  
                    tempnum = array[num-1];  
                    array[num-1] = array[num];  
                    array[num] = tempnum;  
                    tipLabel.setText("Swapping " + tempnum + " and " + array[num-1]);
                    arrayLabel.setText(Arrays.toString(array));
                    this.repaint();
                    swaps++;
                    try
                    {
                        Thread.sleep(500);
                        // this is where I want it to sleep         
                    }
                    catch (InterruptedException e)
                    {

                    }

                } 
            }  
        }  
    }  



    public static void moveToMiddle(JFrame frame)
    {
        GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
        int screenWidth = gd.getDisplayMode().getWidth();
        int screenHeight = gd.getDisplayMode().getHeight();
        frame.setBounds((screenWidth/2) - (frame.getWidth()/2), (screenHeight/2) - (frame.getHeight()/2), frame.getWidth(), frame.getHeight());
    }

    public static void main(String[] args)
    {  
        JPanel panel = new BubbleSort();
        panel.setLayout(null);
        panel.setBackground(Color.WHITE);
        panel.setFocusable(true);

        JFrame frame = new JFrame("Bubble Sort");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);        
        frame.setSize(800, 500);
        frame.setContentPane(panel);
        frame.setVisible(true);
        moveToMiddle(frame);
    }  

    public BubbleSort()
    {
        int arrayLength = 12;
        int maxNumber = 100;
        int minNumber = 0;

        array = new int[arrayLength]; 
        Random r = new Random();
        for(int i=0; i < array.length; i++)
        {
            int num = r.nextInt(maxNumber) + minNumber;
            array[i] = num;
        }

        arrayLabel = new JLabel(Arrays.toString(array));
        arrayLabel.setFont(new Font("Bahnschrift", Font.PLAIN, 35));
        arrayLabel.setBounds(100, 100, 600, 100);

        tipLabel.setBounds(100 , 250 , 600, 100);
        tipLabel.setFont(new Font("Bahnschrift", Font.PLAIN, 35));

        tipLabel.setVerticalAlignment(SwingConstants.CENTER);
        tipLabel.setHorizontalAlignment(SwingConstants.CENTER);

        arrayLabel.setVerticalAlignment(SwingConstants.CENTER);
        arrayLabel.setHorizontalAlignment(SwingConstants.CENTER);

        JButton startButton = new JButton("Start");
        startButton.addActionListener(this);
        startButton.setBounds(0,0,50,50);

        this.add(arrayLabel);   this.add(tipLabel); this.add(startButton);
    }

    @Override
    public void actionPerformed(ActionEvent arg0)
    {
        bubbleSort(array);
    }
}  

I want the panel to update whilst the method is running but using it remains blank until the method has finished.


Solution

  • Change the ActionListner so the sorting is done on a separate thread:

    @Override
    public void actionPerformed(ActionEvent arg0)
    {
        new Thread(()->bubbleSort(array)).start();
    }
    

    To update UI using Swing thread use : SwingUtilities.invokeLater(()->repaint()); instead of this.repaint();