Reputation: 13
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
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