Подтвердить что ты не робот

Как отключить контейнер и его детей в Swing

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

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

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

4b9b3361

Ответ 1

Чтобы добавить к ответу mmyers, отключить детей - непростая задача (см. thread)

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

Технически состояние disable-and-store-old, за которым следует состояние enable-and-restore-to-old, может выглядеть привлекательно. Это даже может быть приятно иметь в особых случаях. Но есть (по крайней мере, вероятно, еще больше) две проблемы с этим.

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

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

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

Попытка решить оба вопроса потребует

a) свойство на соединении: "не трогайте моих детей" и
б) собственность на несостоявшихся иждивенцев: "не трогай меня"

привязка к включенному

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

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

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

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

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

Ответ 3

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

Вы говорите, что обход всех элементов не является вариантом, но мой опыт в том, что этот код хорошо работает для довольно сложных графических интерфейсов. Кстати, если бы у 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);
                }
            }
        }
    }

}

Ответ 4

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

Component[] comps = myPanel.getComponents();
for (Component comp:comps){
    comp.setEnabled(false);
}

Ответ 5

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

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

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

        doYourBusinessHere();
    }
}

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

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

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

Ответ 6

Я бы предложил вам написать рекурсивный метод, который найдет все экземпляры 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);
    }
}