问题
I'm trying to write a program that visualizes simple sorting algorithms using Java Swing.
I have a menu with buttons that let the user choose which sorting algorithm they would like to see. My problem is that repaint() is not calling paintComponent after every index swap, so we don't get to see the array being sorted. Instead, the program just displays the array once the panel is made visible, showing the already sorted array.
I've tried adding frame.revalidate() but it doesn't do anything since I'm not adjusting any frames or panels, just the array.
What am I missing?
Thank you!
Here is my main class and a sorting class (they're all similar).
import java.awt.Color;
import java.awt.Dimension;
import java.awt.CardLayout;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;
public class AlgVisualiser implements ActionListener {
static final int N = 100;
static Integer[] arr;
static final int CONTENT_WIDTH = 800;
static final int CONTENT_HEIGHT = 800;
static JFrame frame = new JFrame("Sorting Algorithms");
static JPanel buttonPanel = new JPanel();
static JPanel arrPanel = new JPanel();
static JButton bubbleButton;
static JButton insertionButton;
static JButton selectionButton;
static Bubble bubbleSort;
static Insertion insertSort;
static Selection selectionSort;
public static void main(String[] args) {
initializeVars();
setFrame();
}
public static void setFrame() {
frame.setLayout(new CardLayout());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
frame.setLocationRelativeTo(null);
buttonPanel.setVisible(true);
frame.add(buttonPanel);
frame.add(arrPanel);
frame.pack();
}
public static void initializeVars() {
arr = new Integer[N];
arr = fillArr(arr);
arr = shuffleArr(arr);
bubbleSort = new Bubble(arr);
bubbleSort.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
insertSort = new Insertion(arr);
insertSort.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
selectionSort = new Selection(arr);
selectionSort.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
AlgVisualiser alg = new AlgVisualiser();
bubbleButton = new JButton("Bubble Sort");
bubbleButton.setPreferredSize(new Dimension(200, 200));
bubbleButton.addActionListener(alg);
selectionButton = new JButton("Selection Sort");
selectionButton.setPreferredSize(new Dimension(200, 200));
selectionButton.addActionListener(alg);
insertionButton = new JButton("Insertion Sort");
insertionButton.setPreferredSize(new Dimension(200, 200));
insertionButton.addActionListener(alg);
bubbleButton.setBackground(Color.WHITE);
selectionButton.setBackground(Color.WHITE);
insertionButton.setBackground(Color.WHITE);
buttonPanel.setBackground(Color.DARK_GRAY);
buttonPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
buttonPanel.add(bubbleButton);
buttonPanel.add(selectionButton);
buttonPanel.add(insertionButton);
arrPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
arrPanel.add(bubbleSort);
}
public void actionPerformed(ActionEvent event) {
if (event.getSource() == bubbleButton) {
buttonPanel.setVisible(false);
arrPanel.setVisible(true);
bubbleSort.sort();
} else if (event.getSource() == selectionButton) {
buttonPanel.setVisible(false);
arrPanel.setVisible(true);
insertSort.sort();
} else if (event.getSource() == insertionButton) {
buttonPanel.setVisible(false);
arrPanel.setVisible(true);
selectionSort.sort();
}
}
public static Integer[] shuffleArr(Integer[] arr) {
arr = fillArr(arr);
List<Integer> list = Arrays.asList(arr);
Collections.shuffle(list);
arr = list.toArray(arr);
return arr;
}
public static Integer[] fillArr(Integer[] arr) {
for (int i = 0; i < N; i++) {
arr[i] = i + 1;
}
return arr;
}
}
import java.awt.*;
import javax.swing.*;
public class Bubble extends JComponent {
private static int checkedIndex1;
private static int checkedIndex2;
private static final long serialVersionUID = 1L;
private Integer[] arr;
public Bubble(Integer[] arr) {
this.arr = arr;
}
public void sort() {
for (int i = 0; i < AlgVisualiser.N - 1; i++) {
for (int j = 0; j < AlgVisualiser.N - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
checkedIndex1 = i;
checkedIndex2 = j;
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
AlgVisualiser.frame.revalidate();
AlgVisualiser.frame.repaint();
}
}
}
checkedIndex1 = -1;
checkedIndex2 = -1;
AlgVisualiser.frame.revalidate();
AlgVisualiser.frame.repaint();
}
@Override
public void paintComponent(Graphics g) {
Graphics2D graphics2d = (Graphics2D) g;
graphics2d.setColor(Color.DARK_GRAY);
graphics2d.fillRect(0, 0, AlgVisualiser.CONTENT_WIDTH, AlgVisualiser.CONTENT_HEIGHT);
for (int i = 0; i < arr.length; i++) {
int width = (int) (AlgVisualiser.CONTENT_WIDTH / (double) AlgVisualiser.N);
int height = arr[i] * (AlgVisualiser.CONTENT_HEIGHT / AlgVisualiser.N);
int x = i * width;
int y = AlgVisualiser.CONTENT_HEIGHT - height;
if (i == checkedIndex1 || i == checkedIndex2) {
graphics2d.setColor(Color.RED);
} else if (checkedIndex1 == -1) {
graphics2d.setColor(Color.GREEN);
} else {
graphics2d.setColor(Color.WHITE);
}
graphics2d.fillRect(x, y, width, height);
}
}
}
回答1:
There are several problems with the posted code:
frame.getContentPane().setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
...
frame.add(buttonPanel);
frame.add(arrPanel);
and
bubbleSort.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
...
arrPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
arrPanel.add(bubbleSort);
You set 3 different components to the same size. They can't all be the same size, since the content pane of the frame contains multiple components.
Don't keep setting the preferred size of components. It is the responsibility of each component to determine its own preferred size.
With components like buttons, if you want the button bigger, you can use the setMargins(...) method.
When doing custom painting, you would override the getPreferredSize()
of the class, since your custom painting code knows best what the size should be.
static JPanel buttonPanel = new JPanel();
static JPanel arrPanel = new JPanel();
static JButton bubbleButton;
static JButton insertionButton;
static JButton selectionButton;
static Bubble bubbleSort;
static Insertion insertSort;
static Selection selectionSort;
Don't use static variable everywhere. This indicates improper design.
public static void initializeVars() {
Again, don't use static methods. Also improper design.
You need to create a class with instance variables and method that can access these instance variables.
graphics2d.fillRect(0, 0, AlgVisualiser.CONTENT_WIDTH, AlgVisualiser.CONTENT_HEIGHT);
Don't access variable from another class when doing painting. Instead you would use the getWidth()
and getHeight()
methods to determine the current size of the component so you can fil in the background of the panel.
AlgVisualiser.frame.revalidate();
AlgVisualiser.frame.repaint();
Don't repaint the entire frame. You are only changing your custom component you you only need to repaint the component, not the entire frame. There is also no need for the reavalidate(). The revalidate() method is used to invoke the layout manager. Your are not adding or removing components from the panel.
the program just displays the array once the panel is made visible, showing the already sorted array.
Now the hard part.
The repaint() method just adds a paint request to the RepaintManager. The RepaintManager will then consolidate requests and add a paint request to the Event Dispatch Thread (EDT) which will repaint the frame.
The problem is your looping code executes so fast that you don't see the individual steps. So you need to use a Thread.sleep(...) so slow down processing to give the GUI a chance to paint each step.
Now you have another problem. If you use Thread.sleep() on the EDT, the GUI still can't repaint() itself until the loop is finished executing.
So you need to execute the soring code on a separate Thread. Then you can tell the sorting Thread to sleep and tell the GUI to repaint itself. One way to do this is to use a SwingWorker
.
Read the section from the Swing tutorial on Concurrency for more information on the EDT
and the SwingWorker
.
来源:https://stackoverflow.com/questions/62668532/repaint-method-not-calling-paintcomponent