Как отключить контейнер и его дочерние элементы в Swing - PullRequest
16 голосов
/ 20 ноября 2008

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

Если я сделаю setEnabled (false) для контейнера, его дочерние элементы все еще будут включены.

Моя структура графического интерфейса довольно сложна, и выполнение обхода всех элементов ниже контейнера невозможно. Стеклянная панель также не находится над контейнером (контейнер - это не все окно).

Ответы [ 6 ]

13 голосов
/ 20 ноября 2008

Чтобы добавить к ответу mmyers , отключение детей - задача не из легких (см. Эту тему )

В общем случае проблема почти неразрешима. Вот почему он не является частью ядра Swing.

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

Составные компоненты

Рекурсия должна останавливаться на «составном компоненте» (или «единственном объекте»). Тогда компонент отвечает за сохранение состояния зависимого. Нет общего способа обнаружить такой компонент - например, JComboBox, JXDatePicker (который как проблема )

Чтобы сделать вещи еще более сложными, иждивенцы не должны находиться в иерархии «составного компонента», например JXTable заботится о включенном состоянии ColumnControl (и заголовка).

Попытка решить обе проблемы потребует

а) свойство комплекса: «не трогай моих детей» и
б) собственность на безусловных иждивенцев: «не трогай меня»

Связывание с включенным

enable-and-update-old-old может нарушить состояние приложения, если включенный статус связан со свойством модели (презентация или другое) и это свойство тем временем изменилось - теперь старое состояние недопустимо.

Попытка заняться тем, что потребует

c) «реальное» свойство «сохранено-старое-включено-из-за-просмотра-проблем»
d) связать свойство модели презентации как с включенным, так и с сохраненным старым включенным

У JXRadioGroup есть вариант этой проблемы: При отключении - сама группа или общий контроллер - отслеживает старое включение каждой кнопки. Кнопка включена контролируется действием - если есть действие. Таким образом, активированному контроллеру необходимо восстановить старый или активированный. Когда группа отключена (as-group), проблема вырисовывается, если для параметра «Действие включено» было ложно при сохранении и было изменено значение «истина». Другое, если добавлены действия.

Теперь представьте сложность переходов между состояниями при перегрузке а) - г)

12 голосов
/ 20 ноября 2008

JXLayer может быть тем, что вы ищете, согласно этой записи :

Оберните ваш контейнер JXLayer и после этого вызовите JXLayer.setLocked(true) - все компоненты внутри будут отключены

альтернативный текст http://www.java.net/download/javadesktop/blogs/alexfromsun/2007.06.25/LayerDemo.PNG

4 голосов
/ 08 декабря 2010

Это код, который я использую. Он рекурсивно посещает дерево компонентов, поддерживая счетчик для каждого компонента. На компонентах хранятся только слабые ссылки, предотвращающие утечку памяти.

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

Пример использования (скобки означают отключение):

     a
    / \
   b   c
      / \
     d   e

setMoreDisabled(c)

     a
    / \
   b  (c)
      / \
    (d) (e)

setMoreDisabled(a)

    (a)
    / \
   b  (c)
      / \
    (d) (e)

setMoreEnabled(a)

     a
    / \
   b  (c)
      / \
    (d) (e)

Теперь код:

import java.awt.Component;
import java.awt.Container;
import java.util.Map;
import java.util.WeakHashMap;

public class EnableDisable {

    private static final Map<Component, Integer> componentAvailability = new WeakHashMap<Component, Integer>();

    public static void setMoreEnabled(Component component) {
        setEnabledRecursive(component, +1);
    }

    public static void setMoreDisabled(Component component) {
        setEnabledRecursive(component, -1);
    }

    // val = 1 for enabling, val = -1 for disabling
    private static void setEnabledRecursive(Component component, int val) {
        if (component != null) {
            final Integer oldValObj = componentAvailability.get(component);
            final int oldVal = (oldValObj == null)
                    ? 0
                    : oldValObj;
            final int newVal = oldVal + val;
            componentAvailability.put(component, newVal);

            if (newVal >= 0) {
                component.setEnabled(true);
            } else if (newVal < 0) {
                component.setEnabled(false);
            }
            if (component instanceof Container) {
                Container componentAsContainer = (Container) component;
                for (Component c : componentAsContainer.getComponents()) {
                    setEnabledRecursive(c,val);
                }
            }
        }
    }

}
0 голосов
/ 08 декабря 2010

Я бы предложил вам написать рекурсивный метод, который находит все экземпляры java.awt.Container в вашем java.awt.Container и устанавливает его компоненты включенными / отключенными. Вот как я решил такую ​​проблему в своем расширенном классе JFrame:

@Override
public void setEnabled(boolean en) {
    super.setEnabled(en);
    setComponentsEnabled(this, en);
}

private void setComponentsEnabled(java.awt.Container c, boolean en) {
    Component[] components = c.getComponents();
    for (Component comp: components) {
        if (comp instanceof java.awt.Container)
            setComponentsEnabled((java.awt.Container) comp, en);
        comp.setEnabled(en);
    }
}
0 голосов
/ 26 февраля 2010

Как ответил VonC, простого решения не существует. Поэтому я рекомендую вам с самого начала программировать со вспомогательной инфраструктурой.

Простая инфраструктура, вероятно, будет, например, использовать делегированных слушателей, которые выполняют проверку "включенного события" из флага суперконтейнера до фактического ответа на событие:

class ControlledActionListener extends ActionListener {
    ...
    public void actionPerformed( ActionEvent e ) {
        if( !container.isEnabled() ) return;

        doYourBusinessHere();
    }
}

Или, что еще лучше, вы можете использовать APT для автоматического ввода кода шаблона.

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

PS. Я хотел бы видеть лучшее решение этой проблемы.

0 голосов
/ 07 февраля 2010

Это то, что я придумал.

Component[] comps = myPanel.getComponents();
for (Component comp:comps){
    comp.setEnabled(false);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...