Как отключить контейнер и его детей в Swing
Я не могу найти способ отключить контейнер и его дочерние элементы в Swing. Действительно ли Swing эта основная функция отсутствует?
Если я устанавливаю setEnabled (false) в контейнере, его дочерние элементы все еще включены.
Моя структура графического интерфейса довольно сложна, и выполнение трассировки всех элементов под контейнером не является вариантом. Также не находится GlassPane поверх контейнера (контейнер - это не все окно).
Ответы
Ответ 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)
Ответ 2
JXLayer может быть тем, что вы ищете, согласно этот пост:
Оберните свой контейнер с помощью JXLayer и вызовите JXLayer.setLocked(true)
после этого - все компоненты внутри будут отключены.
alt text http://www.java.net/download/javadesktop/blogs/alexfromsun/2007.06.25/LayerDemo.PNG
Ответ 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);
}
}