repaint() 方法不调用 paintComponent
repaint() method not calling paintComponent
我正在尝试使用 Java Swing 编写一个可视化简单排序算法的程序。
我有一个带有按钮的菜单,让用户可以选择他们希望看到的排序算法。
我的问题是 repaint() 没有在每次索引交换后调用 paintComponent,所以我们看不到正在排序的数组。相反,程序只在面板可见后显示数组,显示已经排序的数组。
我试过添加 frame.revalidate() 但它没有做任何事情,因为我没有调整任何框架或面板,只是调整数组。
我错过了什么?
谢谢!
这是我的主要 class 和排序 class(它们都很相似)。
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);
}
}
}
发布的代码有几个问题:
frame.getContentPane().setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
...
frame.add(buttonPanel);
frame.add(arrPanel);
和
bubbleSort.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
...
arrPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
arrPanel.add(bubbleSort);
您将 3 个不同的组件设置为相同的大小。它们不能都是相同的大小,因为框架的内容窗格包含多个组件。
不要一直设置组件的首选大小。每个组件都有责任确定自己的首选大小。
对于按钮这样的组件,如果你想让按钮变大,可以使用setMargins(...)方法。
在进行自定义绘画时,您将覆盖 class 的 getPreferredSize()
,因为您的自定义绘画代码最清楚尺寸应该是多少。
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 initializeVars() {
同样,不要使用静态方法。也设计不当。
您需要使用实例变量和可以访问这些实例变量的方法创建一个 class。
graphics2d.fillRect(0, 0, AlgVisualiser.CONTENT_WIDTH, AlgVisualiser.CONTENT_HEIGHT);
绘画时不要从另一个 class 访问变量。相反,您将使用 getWidth()
和 getHeight()
方法来确定组件的当前大小,以便您可以填充面板的背景。
AlgVisualiser.frame.revalidate();
AlgVisualiser.frame.repaint();
不要重新绘制整个框架。您只是在更改自定义组件,您只需要重新绘制组件,而不是整个框架。也不需要 reavalidate()。 revalidate() 方法用于调用布局管理器。您没有在面板中添加或删除组件。
the program just displays the array once the panel is made visible, showing the already sorted array.
现在是困难的部分。
repaint() 方法只是向 RepaintManager 添加一个绘制请求。然后,RepaintManager 将合并请求并将绘制请求添加到将重新绘制帧的事件调度线程 (EDT)。
问题是您的循环代码执行得太快以至于您看不到各个步骤。所以你需要使用 Thread.sleep(...) 来减慢处理速度,让 GUI 有机会绘制每一步。
现在你有另一个问题。如果在 EDT 上使用 Thread.sleep(),GUI 仍然无法重新绘制 () 自身,直到循环执行完毕。
因此您需要在单独的线程上执行排序代码。然后您可以告诉排序线程休眠并告诉 GUI 重新绘制自身。一种方法是使用 SwingWorker
.
阅读有关 Concurrency 的 Swing 教程部分,了解有关 EDT
和 SwingWorker
的更多信息。
我正在尝试使用 Java Swing 编写一个可视化简单排序算法的程序。
我有一个带有按钮的菜单,让用户可以选择他们希望看到的排序算法。 我的问题是 repaint() 没有在每次索引交换后调用 paintComponent,所以我们看不到正在排序的数组。相反,程序只在面板可见后显示数组,显示已经排序的数组。
我试过添加 frame.revalidate() 但它没有做任何事情,因为我没有调整任何框架或面板,只是调整数组。
我错过了什么?
谢谢!
这是我的主要 class 和排序 class(它们都很相似)。
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);
}
}
}
发布的代码有几个问题:
frame.getContentPane().setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
...
frame.add(buttonPanel);
frame.add(arrPanel);
和
bubbleSort.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
...
arrPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
arrPanel.add(bubbleSort);
您将 3 个不同的组件设置为相同的大小。它们不能都是相同的大小,因为框架的内容窗格包含多个组件。
不要一直设置组件的首选大小。每个组件都有责任确定自己的首选大小。
对于按钮这样的组件,如果你想让按钮变大,可以使用setMargins(...)方法。
在进行自定义绘画时,您将覆盖 class 的 getPreferredSize()
,因为您的自定义绘画代码最清楚尺寸应该是多少。
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 initializeVars() {
同样,不要使用静态方法。也设计不当。
您需要使用实例变量和可以访问这些实例变量的方法创建一个 class。
graphics2d.fillRect(0, 0, AlgVisualiser.CONTENT_WIDTH, AlgVisualiser.CONTENT_HEIGHT);
绘画时不要从另一个 class 访问变量。相反,您将使用 getWidth()
和 getHeight()
方法来确定组件的当前大小,以便您可以填充面板的背景。
AlgVisualiser.frame.revalidate();
AlgVisualiser.frame.repaint();
不要重新绘制整个框架。您只是在更改自定义组件,您只需要重新绘制组件,而不是整个框架。也不需要 reavalidate()。 revalidate() 方法用于调用布局管理器。您没有在面板中添加或删除组件。
the program just displays the array once the panel is made visible, showing the already sorted array.
现在是困难的部分。
repaint() 方法只是向 RepaintManager 添加一个绘制请求。然后,RepaintManager 将合并请求并将绘制请求添加到将重新绘制帧的事件调度线程 (EDT)。
问题是您的循环代码执行得太快以至于您看不到各个步骤。所以你需要使用 Thread.sleep(...) 来减慢处理速度,让 GUI 有机会绘制每一步。
现在你有另一个问题。如果在 EDT 上使用 Thread.sleep(),GUI 仍然无法重新绘制 () 自身,直到循环执行完毕。
因此您需要在单独的线程上执行排序代码。然后您可以告诉排序线程休眠并告诉 GUI 重新绘制自身。一种方法是使用 SwingWorker
.
阅读有关 Concurrency 的 Swing 教程部分,了解有关 EDT
和 SwingWorker
的更多信息。