Что-то не так с реализацией Swing MVC для JList? - PullRequest
11 голосов
/ 17 января 2011

Некоторое время назад я задал этот вопрос .Все решения - обходные пути.

Теперь этого не может быть.Я чувствую, что здесь что-то не так, но я не могу сказать, концептуально ли это модель MVC Swing или концептуально неправильна моя мысль.

Здесь снова проблема.Я использую JList, чтобы реализовать список миниатюр для страниц документа.Если пользователь выбирает другой эскиз из списка, эта страница загружается.Для этого я добавил ListSelectionListener к JList, который при изменении выбора загружает эту страницу.Но пользователь также может изменить страницу, используя другой элемент управления.Естественно, я хочу, чтобы это было отражено в списке миниатюр, выбрав эту страницу здесь.Поэтому я setSelectedIndex() чтобы обновить JList.К сожалению, это приводит к нежелательному эффекту поднятия ListSelectionEvent, что заставляет слушателя перезагрузить страницу.

Что здесь не так?Я просто изменил модель откуда-то еще, поэтому, естественно, я хочу, чтобы представление само обновлялось, но я не хочу, чтобы оно вызывало события.Разве Swing не реализует MVC правильно?Или я здесь упускаю точку?

Ответы [ 7 ]

9 голосов
/ 17 января 2011

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

Проблема с Swing в этом отношениичто часть контроллера MVC часто несколько разделяется между компонентом представления и моделью, поэтому может быть сложно централизовать эту логику.В некоторой степени интерфейс Action исправляет это для событий actionPerformed (), помещая логику в одно место и позволяя ей делиться с другими компонентами, но это не помогает для других типов событий, или когда есть несколькоразличные классы событий, которые должны быть скоординированы.

В таком случае ответ заключается в том, чтобы следовать шаблону, на который намекает Swing, но который не сделан явным: выполнять запрошенное обновление, только если состояние действительно изменится, в противном случае -ничего такого.Примером этого является сам JList: если вы попытаетесь установить выбранный индекс JList на тот же индекс, который уже выбран , ничего не произойдет .События не будут запущены, обновления не произойдут: запрос на обновление фактически игнорируется.Это хорошая вещь.Это означает, что вы можете, например, иметь на вашем JList слушателя, который будет реагировать на вновь выбранный элемент, а затем, в свою очередь, попросить того же JList повторно выбрать этот же элемент, и вы не застрянете впатологически рекурсивная петля.Если все контроллеры моделей в приложении делают это, то не возникает проблем с несколькими повторяющимися событиями, которые запускаются повсюду - каждый компонент будет обновлять себя (и впоследствии запускать события) только в случае необходимости и при обновлении.тогда он может запустить все нужные ему события обновления, но только те компоненты, которые еще не получили сообщение, с этим поделают.

5 голосов
/ 17 января 2011

Это ожидаемое поведение.

С Модель-Вид-Контроллер [Википедия] :

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

Итак, когда вы звоните setSelectedIndex на JList, вы обновляете его модель, которая затем уведомляет каждого ListSelectionListener. Это не было бы MVC, если бы вы могли «тихо» обновить модель, не сообщая об этом никому.

3 голосов
/ 17 января 2011

Swing не совсем MVC, но имеет свои корни в MVC (разница заключается в том, что представление и контроллер в Swing более тесно связаны, чем в других MVC, см. Архитектура Swing для получения более подробной информации. ).

Но может показаться, что это не та проблема, с которой вы столкнулись. Похоже, вы должны проверить в слушателях событий тип события и решить, следует ли его игнорировать: если событие возникло в списке, измените его. Если был вызван какой-то другой контроль, не надо.

2 голосов
/ 07 июня 2012

Я всегда так делаю:

    public class MyDialog extends JDialog {
        private boolean silentGUIChange = false;

    public void updateGUI {
        try {
            silenGUIChange = true;

            // DO GUI-Updates here:
            textField.setText("...");
            checkBox.setSelected (...);

        }
        finally {
            silentGUIChange = false;
        }
    }

    private void addListeners () {
        checkBox.addChangeListener (new ChangeListener () {
           public void stateChanged (ChangeEvent e) {
              if (silentGUIChange)
                 return;

              // update MODEL
              model.setValue(checkBox.isSelected());
          }
         });
    }

}
2 голосов
/ 18 января 2011

Я все еще чувствую, что здесь что-то концептуально не так.

Я сочувствую, но это может помочь понять, что у вас нет простого JList наблюдения ListSelectionModel.Вместо этого у вас есть JList и некоторый другой элемент управления, наблюдающий гибридную модель отбора.В примере @ Taisin гибрид представляет собой CustomSelectionModel, который расширяет DefaultListSelectionModel и допускает бесшумные изменения.При совместимости можно также поделиться моделью, как предлагается в этом вопросе и ответе и в этом SharedModelDemo из учебного пособия.

Для справки это thread цитирует статью Дизайн приложения Java SE с MVC: проблемы с дизайном приложения , в котором более подробно рассматривается проблема.

2 голосов
/ 17 января 2011

Что сказал @dogbane.

Но чтобы решить проблему, вам нужно добавить какую-то проверку состояния во время прослушивателя, чтобы увидеть, является ли событие тем, которое вы должны игнорировать.ListSelectionEvent имеет метод getValueAdjusting (), но он в основном внутренний.Что вам нужно сделать, это смоделировать это самостоятельно.

Так, например, когда вы обновляете список из внешнего выбора, вы должны иметь код, подобный ...

try {
    setSelectionAdjusting(true);
    /* ... your old update code ... */
} finally {
    setSelectionAdjusting(false);
}

и в коде обновления для ListSelectionListenerEvent

public void valueChanged(ListSelectionEvent e) {
    if (!isSelectionAdjusting()) {
        /* ... do what you did before ...*/
    }
}

Вопросы определения объема и доступа оставлены в качестве упражнения для читателя.Вам придется написать setSelectionAdjusting и, возможно, установить его также для других объектов.

0 голосов
/ 17 января 2011

Обычно слушатель работает так, что он «гаснет» каждый раз, когда происходит ожидаемое событие.Если бы мне пришлось размышлять, это недоразумение с вашей стороны.

...