Isiah Redentor
Isiah Redentor

Reputation: 13

Animation of java.awt rectangles not acting as wanted

I am trying to animate the process of the Selection & Insertion sort algorithms with rectangles displaying sorted values by height.

I call repaint() when a single change occurs in my Select/Insert sort algorithms, but it does not seem to repaint them entirely, only some of the rectangles change. In the end result, it doesn't actually display the fully sorted array at all.

Both algorithms are tested and work, so the problems seems to be with animating their process.

Any help?

MyPaint.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Timer;
import java.util.TimerTask;
public class MyPanel extends JPanel{
  private static int[] mainArr;
  private boolean reset = false;
  private Timer t = new Timer();
  private int[] tempArr;
    public MyPanel(){
       JPanel panel = new JPanel();
       panel.setPreferredSize(new Dimension(400, 400));
       panel.setBackground(Color.WHITE);
       panel.setLayout(new FlowLayout(FlowLayout.CENTER));
       //JButton selButton = new JButton("Select Sort");
       //panel.add(selButton);
       add(panel);      
    }   
    public static void main(String[] args) {
      SortDriver frame = new SortDriver();
      mainArr = frame.getArr();
      frame.setVisible(true);

    }
}

SortDriver.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics;
public class SortDriver extends JFrame implements ActionListener {

        private int modeName;
        private JButton startButton;
        private JButton selButton;
        private JButton insButton;
        private JPanel mainPanel;
        private MyPanel panel;
        private int[] sortArr = new int[41];

        public SortDriver() {
                setLayout(null);
                setPreferredSize(new Dimension(420, 420));
                setResizable(false);
                setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                mainPanel = new JPanel();
                mainPanel.setBackground(Color.WHITE);
                mainPanel.setBounds(0, 0, 420, 420);
                mainPanel.setPreferredSize(new Dimension(400, 400));
                //mainPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
                add(mainPanel);

                //Buttons 
                startButton = new JButton("Start");
                startButton.addActionListener(this);
                mainPanel.add(startButton);
                selButton = new JButton("Select Sort");
                selButton.addActionListener(this);
                mainPanel.add(selButton);
                insButton = new JButton("Insert Sort");
                insButton.addActionListener(this);
                mainPanel.add(insButton);


                //Panel
                panel = new MyPanel();

                mainPanel.add(panel);
                //Array                              
                for(int i = 0; i <= 40; i++){
                  sortArr[i] = 0; 
                }
                for(int i = 0; i <= 40; i++){
                  int random = (int)(Math.random() * 50 + 1);
                  sortArr[i] = random; 
                }
                //Final
                pack();
        }
        public void paint(Graphics g) {
          for(int i = 0; i <= 40; i++){
            g.fillRect(i * 10, 100, 5, sortArr[i]);
          }      
        }

        public void selectionSort (int[] list){       
          int min;       
          int temp;        
          for (int index = 0; index < list.length-1; index++){          
            min = index;          
            for (int scan = index+1; scan < list.length; scan++)             
              if (list[scan] - (list[min]) < 0) min = scan;           

      // Swap the values          
            temp = list[min];          
            list[min] = list[index];
            list[index] = temp;
            repaint();
          }
          //for(int i = 0; i <= list.length; i++){
          //  System.out.println(list[i]); 
          //}
        }
        public void insertionSort (int[] list){       
          for (int index = 1; index < list.length; index++){          
            int key = list[index];          
            int position = index;           

            //  Shift larger values to the right          
            while (position > 0 && key - (list[position-1]) < 0){             
              list[position] = list[position-1];
              position--;
              repaint();
            }          
            list[position] = key;       
          }    
        }
        public void actionPerformed(ActionEvent event) {
          if (event.getSource() == selButton) {
            modeName = 1;
          }
          else if (event.getSource() == insButton) {
            modeName = 2;
          }
          else if (event.getSource() == startButton) {
            if(modeName == 1){
              selectionSort(sortArr);
            }
            if(modeName == 2){
              insertionSort(sortArr);
            }
          }
        }
        public int getMode(){
          return modeName; 
        }
        public int[] getArr(){
          return sortArr; 
        }
}

Upvotes: 1

Views: 88

Answers (1)

Calculator
Calculator

Reputation: 2789

First of all, you should not override the paint method of your JFrame. Rather override paintComponent() of a JPanel for customize drawing. You could use MyPanel for this purpose:

public class MyPanel extends JPanel{

    public MyPanel(){
       setBackground(Color.WHITE);             
    }   

    public synchronized void paintComponent(Graphics g) {
        super.paintComponent(g);
        // your paint code    
    }

}

Secondly, avoid using null layout. Better use something like BorderLayout in your case for the JFrame.

Also, you have to be aware of the Event Dispatch Thread. Manipulations on GUI elements have to happen on this thread.

You should setup your GUI on the EDT. You can do this using SwingUtilities.invokeLater() in your main-method:

SwingUtilities.invokeLater(() -> {
    SortDriver frame = new SortDriver();
    mainArr = frame.getArr();
    frame.setVisible(true);
});

You should consider to handle animation in a separate thread so that sorting happens outside of the EDT. Here is a small demo on how it could work in principle featuring your selection sort:

new Thread(() -> {
  int min;       
  int temp;  

  final int delayMillis = 100;
  long startTickTime = System.nanoTime();
  for (int index = 0; index < list.length-1; index++){          
    synchronized(myPanel){
        min = index;          
        for (int scan = index+1; scan < list.length; scan++)             
          if (list[scan] - (list[min]) < 0) min = scan;           

        // Swap the values          
        temp = list[min];          
        list[min] = list[index];
        list[index] = temp;
    }
    myPanel.repaint();
    try {
        TimeUnit.NANOSECONDS.sleep(delayMillis*1000000-System.nanoTime()+startTickTime);
        startTickTime = System.nanoTime();
    } catch (Exception e) {                 
        e.printStackTrace();
    }
  }
}).start();

As you can see I put the code for updating the selection sort into a synchronized block so that it is synchronized with the paintComponent() method. This is necessary, because painting happens on the EDT while sorting happens on a thread different from the EDT. In the example I used the MyPanel object as monitor.

Upvotes: 1

Related Questions