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

时间:2014-03-26 18:32:44

标签: 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(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;
    }

}

此显示也用于每秒更新时间。显示的值在方法setNumber()中设置。问题是当更新例如从9到10或从79到80时,可见第一个负责10 ^ 1值的JLabel是更新,而仅10 ^ 0值JLabel之后。

有没有办法如何实现所有JLabels始终从用户的角度同时更新?

编辑:

1.Simple Swing计时器代码:

 ActionListener taskPerformer = new ActionListener() {
          public void actionPerformed(ActionEvent evt) {
             display.setNumber(secondsPassed);
          }
      };
 new Timer(1000, taskPerformer).start();

2. @ Marco13感谢你指出那种愚蠢的初学者般的抑扬失误。

3. @ user1803551问题是在JLabel.setIcon()JLabel.repaint()被调用。 这意味着如果我逐个设置图标,JLabels也可以逐个更新。我不确定JLabel.setText()

我试图将SSCCE放在一起,但是所有的标签都立刻被涂上了,问题不明显可能是太简单的例子了。我不能说因为我没有关于Swing绘画内部的知识。

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

1 个答案:

答案 0 :(得分:0)

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

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的返回,这需要一些时间,sleep也可能不会准确,但它只是一个模拟。如果有人可以建议在后台运行计时器的正确方法(更新某些组件),那就太好了。