Правильно ли обновляете компоненты Swing?

Я новичок в качелях, любая помощь приветствуется.

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

На данный момент что происходит: 1. при нажатии на первую карту переворачивается 2. при нажатии на вторую карту происходит одно из двух событий (а) если они одинаковы, они оба остаются наверху, что я и хочу (б) если они не одинаковы. Я вообще никогда не вижу вторую карту, поскольку она сразу же повторно отображает обратную сторону карты (и обратную сторону предыдущей карты также, как определено в моем методе).

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

Я попытался использовать contentPane.revalidate(); & contentPane.repaint(); но это ничего не меняет.

Я добавил несколько выводов консоли:

Console output:
Card: 0 set
Card: 6 set
Sleeping now
Card: 6 unset
Card: 0 unset

Выше приведен вывод консоли при нажатии на две карты, которые не совпадают

@Override
public void actionPerformed(ActionEvent e) 
{
    String buttonPressed = e.getActionCommand();
    int pos = Integer.valueOf(buttonPressed);
    action = Control.model.ReceiveCardsTurned(pos);

    keypadArray[pos].setIcon(myIcons[pos]);     
    System.out.println("Card: "+pos+" set");
    currentTime.setText("" + Control.model.time);
    currentScore.setText("" + Control.model.score);

    //contentPane.revalidate();
    //contentPane.repaint();        

    if(Control.model.twoCardsTurned == false)
    {
        if (action == "unturn") 
        {
            System.out.println("Sleeping now");

            try 
            {
                Thread.sleep(1000);
            }

            catch (InterruptedException e1) 
            {
                e1.printStackTrace();
            }

            keypadArray[pos].setIcon(back);
            keypadArray[Control.model.lastCard].setIcon(back);
            System.out.println("Card: "+pos+" unset");
            System.out.println("Card: "+Control.model.lastCard+" unset");
        }
    }
}

person Ron    schedule 14.12.2012    source источник


Ответы (2)


Есть ряд важных понятий, которые вы, кажется, упускаете из виду.

  1. Swing — это среда, управляемая событиями. Это означает, что нет средств (или, по крайней мере, очень мало), чтобы вы могли «ждать» ввода пользователя, как правило, вам просто нужно реагировать на их взаимодействие.
  2. Swing управляется одним потоком, известным как поток диспетчеризации событий (также известный как EDT). Этот поток отвечает за отправку/обработку событий, поступающих в приложение, в соответствующие части приложения, чтобы они могли принять меры.
  3. Менеджер перерисовки отправляет свои запросы на обновление в EDT.

ЛЮБОЕ действие, которое вы предпримете и которое помешает EDT выполнить эту работу, создаст впечатление, что ваше приложение зависло.

Вы НИКОГДА не должны выполнять какие-либо трудоемкие операции (например, ввод-вывод, циклы или Thread#sleep) в EDT, это приведет к «паузе» вашего приложения, что никогда не бывает красивым.

Прочтите параллелизм в Swing для получения дополнительной информации.

Теперь у вас есть несколько вариантов. Вы можете использовать Thread, чтобы «подождать» в фоновом режиме и перевернуть карты, или вы можете использовать SwingWorker или javax.swing.Timer.

Другая проблема, которая у вас есть, заключается в том, что вы НИКОГДА не должны обновлять какие-либо компоненты пользовательского интерфейса из любого Thread, кроме EDT. Это означает, что если вы будете использовать Thread, вы будете нести ответственность за повторную синхронизацию этого потока с EDT. Хотя это не сложно, это просто становится грязным.

SwingWorker и javax.swing.Timer имеют функции, которые значительно упрощают эту задачу.

Потоки и SwingWorker отлично подходят для выполнения фоновой обработки и были бы просто излишними для этой проблемы. Вместо этого javax.swing.Timer идеально подошла бы сюда.

if (!Control.model.twoCardsTurned)
    {
        if ("unturn".equals(action)) 
        {
            new Timer(1000, new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    keypadArray[pos].setIcon(back);
                    keypadArray[Control.model.lastCard].setIcon(back);
                    System.out.println("Card: "+pos+" unset");
                    System.out.println("Card: "+Control.model.lastCard+" unset");
                }
            }).start();
        }
    }

Это действительно простой пример. Возможно, вы захотите добавить некоторые элементы управления, которые не позволят пользователю щелкнуть что-либо, пока, например, не сработает таймер;)

person MadProgrammer    schedule 14.12.2012
comment
Еще раз спасибо, очень полезный и очень информативный ответ. Мне нужно будет еще немного почитать об основных концепциях свинга. - person Ron; 14.12.2012

Вы не можете спать в потоке отправки событий, потому что ваш графический интерфейс зависнет. Вы должны использовать Swing Timer. Для фоновых задач, о которых вам, вероятно, придется беспокоиться в будущем, взгляните на SwingWorker.

person Behnil    schedule 14.12.2012
comment
@HovercraftFullOfEels Я улучшил ответ, спасибо за ваше замечание. - person Behnil; 14.12.2012
comment
Я согласен с судном на воздушной подушке, я думаю, что SwingWorker больше не убивать для этой простой проблемы. - person MadProgrammer; 14.12.2012
comment
Спасибо за помощь дружище - person Ron; 14.12.2012
comment
в конце концов, я использовал SwingWorker ... это было необходимо позже, как вы и предсказывали behnill ... спасибо - person Ron; 15.12.2012