Java repaint()方法不调用paintComponent

Java repaint()方法不调用paintComponent,java,swing,awt,Java,Swing,Awt,我正试图编写一个程序,使用JavaSwing可视化简单的排序算法 我有一个带有按钮的菜单,让用户选择他们希望看到的排序算法。 我的问题是,repaint在每次索引交换后都不会调用paintComponent,因此我们无法看到数组被排序。相反,一旦面板可见,程序只显示数组,显示已排序的数组 我已经尝试添加frame.revalidate,但它没有任何作用,因为我没有调整任何帧或面板,只是调整数组 我错过了什么 谢谢大家! 这是我的主类和排序类,它们都很相似 import java.awt.Colo

我正试图编写一个程序,使用JavaSwing可视化简单的排序算法

我有一个带有按钮的菜单,让用户选择他们希望看到的排序算法。 我的问题是,repaint在每次索引交换后都不会调用paintComponent,因此我们无法看到数组被排序。相反,一旦面板可见,程序只显示数组,显示已排序的数组

我已经尝试添加frame.revalidate,但它没有任何作用,因为我没有调整任何帧或面板,只是调整数组

我错过了什么

谢谢大家!

这是我的主类和排序类,它们都很相似

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;
    }
}


发布的代码有几个问题:

    frame.getContentPane().setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
    ...
    frame.add(buttonPanel);
    frame.add(arrPanel);

将3个不同的组件设置为相同的大小。它们不能都是相同的大小,因为框架的内容窗格包含多个组件

不要一直设置组件的首选尺寸。每个部件都有责任确定自己的首选尺寸

对于按钮之类的组件,如果希望按钮更大,可以使用setMargins。。。方法

在进行自定义绘制时,您将重写类的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() {
同样,不要使用静态方法。还有设计不当

public static void initializeVars() {
您需要使用实例变量和可以访问这些实例变量的方法创建一个类

graphics2d.fillRect(0, 0, AlgVisualiser.CONTENT_WIDTH, AlgVisualiser.CONTENT_HEIGHT);
进行绘制时,不要从其他类访问变量。相反,您可以使用getWidth和getHeight方法来确定组件的当前大小,以便可以在面板的背景中填充

    AlgVisualiser.frame.revalidate();
    AlgVisualiser.frame.repaint();
不要重新粉刷整个框架。您只需更改自定义组件,只需重新绘制组件,而不需要重新绘制整个框架。也不需要重新验证。revalidate方法用于调用布局管理器。您没有在面板中添加或删除组件

    AlgVisualiser.frame.revalidate();
    AlgVisualiser.frame.repaint();
一旦面板可见,程序只显示数组,显示已排序的数组

现在是最难的部分

repaint方法只是向RepaintManager添加一个绘制请求。然后,RepaitManager将合并请求,并将绘制请求添加到事件调度线程EDT,该线程将重新绘制帧

问题是循环代码执行得太快,以至于看不到各个步骤。所以你需要用一根线睡觉。。。因此,放慢处理速度,让GUI有机会绘制每个步骤

现在你有另一个问题了。如果在EDT上使用Thread.sleep,GUI在循环执行完成之前仍然无法重新绘制自身

因此,您需要在单独的线程上执行排序代码。然后,您可以告诉排序线程休眠,并告诉GUI重新绘制自己。一种方法是使用SwingWorker


有关EDT和SwingWorker的更多信息,请阅读上的Swing教程部分。

Swing基于事件。在ActionListener返回之前,不会处理任何事件。这包括重新绘制事件。因此,在调用sort的ActionListener返回之前,所有重绘调用都不会生效。您需要使用一个或一个新线程,这样排序就不会占用AWT事件调度线程。在我的SwingWorker类中,doInBackground是否应该完成排序并发布我希望由paintComponent绘制的每个不同数组,然后调用repaint来绘制我们发布的每个数组?我不知道如何使用SwingWorker来实现这一点。是的,doInBackground将发布数组的每个迭代,因此您需要在发布时制作一个副本。process方法将接收数组并使用数组的新状态更新组件,然后重新绘制自身。您不需要实现done方法,因为组件会在结果发布时不断地重新绘制自身。