Как обновить сразу несколько компонентов Swing (JLabel) - предотвратить обновление/перекраску по одному

У меня простой класс отображения. Каждая цифра представлена одним 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, видно, что первый JLabel ответственный за значение 10 ^ 1, обновляется и 10 ^ 0 значение JLabel только после этого.

Есть ли способ добиться того, чтобы все JLabels постоянно обновлялись с точки зрения пользователя?

РЕДАКТИРОВАТЬ:

1. Простой код таймера поворота:

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 ответ

Я переписал части вашего кода, чтобы сделать его более коротким и эффективным, а также смоделировал таймер, работающий в фоновом режиме. Я должен был заменить ваши значки регулярными строками.

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();
 }
}
</void,>

Я считаю, что этот способ подсчета секунд не является хорошим, потому что вызовы внутри рабочего потока ожидают возврата вызова setNumber в потоке отправки событий, который занимает некоторое время, а также sleep может быть не слишком точным, но это просто симуляция. Если кто-то может предложить правильный способ запуска таймера в фоновом режиме (который обновляет какой-то компонент), было бы неплохо.

licensed under cc by-sa 3.0 with attribution.