Java 如何一次更新多个Swing组件(JLabel)-防止逐个更新/重新绘制

Java 如何一次更新多个Swing组件(JLabel)-防止逐个更新/重新绘制,java,swing,Java,Swing,我有一个简单的显示类。每个数字由一个JLabel表示,设置图标从0到9 public class Display extends JPanel{ private static final int DIGIT_COUNT = 3; private JLabel[] labels = new JLabel[DIGIT_COUNT]; private Icon[] numbers = ResourceManager.getInstance().createIconSet(Im

我有一个简单的显示类。每个数字由一个JLabel表示,设置图标从0到9

public class Display extends JPanel{

    private static final int DIGIT_COUNT = 3;
    private JLabel[] labels = new JLabel[DIGIT_COUNT];
    private Icon[] numbers = ResourceManager.getInstance().createIconSet(ImageSetResource.DISPLAY_NUMBERS,13);


    public Display(){
        GridLayout layout = new GridLayout(1,DIGIT_COUNT);
        setLayout(layout);
        for (int i = 0; i < labels.length; i++) {
            labels[i] = new JLabel();
            labels[i].setIcon(numbers[0]);
            add(labels[i]);
        }

        setNumber(0);
    }

    public void setNumber(int number){
        int max = 10 ^ DIGIT_COUNT -1;
        if (number < 0 && number > max)
            throw new IllegalArgumentException("The number needs to be in range <0, " + max + ">");

        int[] digits = getDigitArray(number);

        for (int i = 0; i < digits.length; i++) {
            labels[i].setIcon(numbers[digits[i]]);
        }

    }

    private int[] getDigitArray(int number){
        int[] digits = new int[DIGIT_COUNT];

        int leftover = number;
        for (int i = 0; i < DIGIT_COUNT; i++){
            int result = leftover % 10;
            leftover = (leftover - result)/10;
            digits[DIGIT_COUNT -1 -i] = result;

            if (leftover == 0)
                break;
        }
        assert leftover == 0;

        return digits;
    }

}
2.@Marco13谢谢你指出那个愚蠢的初学者喜欢的扬抑错误

3.@user1803551问题是在JLabel.setIcon中调用了JLabel.repaint。 这意味着,如果我一个接一个地设置图标,JLabel也可以一个接一个地更新。但是我不确定JLabel.setText是否正确

我试着把SSCCE放在一起,但是所有的标签都一次画好了,问题不明显,可能太简单了。我不能说,因为我不具备关于秋千绘画内部的知识


我正在寻找一种方法,以某种方式禁用从setIcon方法调用的组件上的重新绘制,并立即重新绘制特定组组件。

我重写了部分代码,使其更短、更高效,并模拟了在后台运行的计时器。我不得不用常规字符串替换你的图标

public class Display extends JPanel{

    private static final int DIGIT_COUNT = 6;
    private static final int MAX = (int) Math.pow(10, DIGIT_COUNT) - 1;
    private JLabel[] labels = new JLabel[DIGIT_COUNT];
    private String[] numbers = new String[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};

    public Display(){

        for (int i = 0; i < DIGIT_COUNT; i++)
            add(labels[i] = new JLabel(numbers[0]));
    }

    public void setNumber(int number){

        if (number < 0 && number > MAX)
            throw new IllegalArgumentException("The number needs to be in range <0, " + MAX + ">");

        for (int i = 0; i < DIGIT_COUNT; i++) 
            labels[DIGIT_COUNT - i - 1].setText(numbers[(number / (int) Math.pow(10, i)) % 10]);
    }

    public static void main(String[] args) {

        JFrame frame = new JFrame();
        final Display dis = new Display();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(dis);
        frame.pack();
        frame.setVisible(true);

        new SwingWorker<Void, Integer>() {

            @Override
            protected Void doInBackground() {

                int x = 0;
                while (x <= MAX) {
                    dis.setNumber(x);
                    x++;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }
        }.execute();
    }
}

我认为这种计算秒数的方法不好,因为工作线程内的调用等待事件分派线程上调用setNumber的返回,这需要一些时间,而且睡眠可能不太准确,但这只是一种模拟。如果有人能建议在后台运行计时器以更新某些组件的正确方法,那就太好了。

这篇文章演示了这个问题。旁白:我很确定int max=10^DIGIT\u COUNT-1;不是在做你认为它能做的事。它的计算结果不是数字计数的10次方-^是一个按位异或,这里的结果很可能是垃圾。这甚至可能与问题有关,但是。。。谁知道。。您可能应该使用int max=intMath.pow10,数字\u COUNT-1;相反,我试图将SSCCE组装在一起,但所有的标签都同时被涂上了…-这就是为什么要创建SSCCE来简化问题。现在,您必须查看真正的代码,看看有什么区别。我们无法帮助您,因为我们不知道您真正的代码是什么样子的。并立即重新绘制特定的组组件-这是Swing的默认行为。RepaintManger接收绘制请求并将它们分组以执行一次重新绘制。如果它们没有一起绘制,那么在设置每个标签的图标之间会有一些时间延迟。实际上,当所有实际代码都发布在这里时,情况就是这样,因为它并不长。它包含在setNumber方法中,我还包含了计时器代码。我的问题是,我甚至不能说这是正常行为还是我的代码中有什么不好的地方。我只是看不出有什么不对劲。后续的绘制请求都是在事件调度线程上完成的。如果它在SSCCE中工作,那么您就证明它按预期工作。如果它在你真正的程序中不起作用,那么你正在做一些不同/错误的事情。为什么在实际应用程序中代码的长度会发生变化?更新标签的定时器代码应该是相同的。@ LeNoob,如果这个答案解决了你的问题,请考虑接受它,或者如果有帮助的话,进行投票。
public class Display extends JPanel{

    private static final int DIGIT_COUNT = 6;
    private static final int MAX = (int) Math.pow(10, DIGIT_COUNT) - 1;
    private JLabel[] labels = new JLabel[DIGIT_COUNT];
    private String[] numbers = new String[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};

    public Display(){

        for (int i = 0; i < DIGIT_COUNT; i++)
            add(labels[i] = new JLabel(numbers[0]));
    }

    public void setNumber(int number){

        if (number < 0 && number > MAX)
            throw new IllegalArgumentException("The number needs to be in range <0, " + MAX + ">");

        for (int i = 0; i < DIGIT_COUNT; i++) 
            labels[DIGIT_COUNT - i - 1].setText(numbers[(number / (int) Math.pow(10, i)) % 10]);
    }

    public static void main(String[] args) {

        JFrame frame = new JFrame();
        final Display dis = new Display();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(dis);
        frame.pack();
        frame.setVisible(true);

        new SwingWorker<Void, Integer>() {

            @Override
            protected Void doInBackground() {

                int x = 0;
                while (x <= MAX) {
                    dis.setNumber(x);
                    x++;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }
        }.execute();
    }
}