Я не могу найти способ отключить контейнер И его дочерние элементы в Swing. Действительно ли Swing не хватает этой базовой функции?
Если я сделаю setEnabled (false) для контейнера, его дочерние элементы все равно будут включены.
Моя структура графического интерфейса довольно сложна, и обход всех элементов ниже контейнера не вариант. Также нет GlassPane поверх контейнера (контейнер - это не все окно).




Чтобы добавить к Ответ mmyers, отключение детей - непростая задача (см. Этот нить)
The problem is near-to unsolvable in the general case. That's why it is not part of core Swing.
Technically, the disable-and-store-old-state followed by a enable-and-restore-to-old-state might look attractive. It even might be a nice-to-have in special cases. But there are (at least, probably a bunch more) two issues with that.
Compound components
The recursion must stop on a "compound component" (or "single entity"). Then the component is responsible for keeping dependent's state. There's no general way to detect such a component - examples are JComboBox, JXDatePicker (which as related issue)
To make things even more complicated, dependents don't need to be under the hierarchy of the "compound component", f.i. JXTable takes care of the ColumnControl's (and header's) enabled state.
Trying to tackle both would require to have
a) a property on the compound: "don't touch my children" and
b) a property on the uncontained dependents: "don't touch me"Binding to enabled
enable-and-update-to-old might break application state if the enabled status is bound to a (presentation or other) model property and that property changed in-the-meantime - now the old-state is invalid.
Trying to tackle that would require to have
c) a "real" stored-old-enabled-due-to-view-concerns property
d) bind the presentation model property to both the enabled and the stored-old-enabledJXRadioGroup has a variant of that problem: On disabling - the group itself or the general controller - keeps track of the old-enabled of every button. Button's enabled is controlled by the Action - if there is an Action. So the enabled controller needs to restore to old-enabled or to action's enabled. During group's disabled (as-group) a problem looms if the Action's enabled was false on storing and changed to true. Another if actions are added.
Now imagine the complexity of state transitions when overloading a)-- d)
Вот что я придумал.
Component[] comps = myPanel.getComponents();
for (Component comp:comps){
comp.setEnabled(false);
}
Как ответил VonC, простого решения не существует. Поэтому я рекомендую вам с самого начала программировать с помощью вспомогательной инфраструктуры.
Простая инфраструктура, вероятно, будет, например, с использованием делегированных слушателей, которые выполняют проверку «включено событие» из флага суперконтейнера перед фактическим ответом на событие:
class ControlledActionListener extends ActionListener {
...
public void actionPerformed( ActionEvent e ) {
if ( !container.isEnabled() ) return;
doYourBusinessHere();
}
}
Или, что еще лучше, вы можете использовать APT для автоматического внедрения стандартного кода за вас.
Это всегда хорошо работает. Это чистый способ заблокировать как взаимодействие с пользователем, так и вызовы программирования одним усилием. Несмотря на то, что для поддержки базовой функциональности вам потребуется несколько кодов, взамен вы получите простоту, удобство использования и стабильность.
PS. я хотел бы увидеть лучшее решение этой проблемы.
Я бы посоветовал вам написать рекурсивный метод, который находит все экземпляры 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);
}
}
Это код, который я использую. Он рекурсивно посещает дерево компонентов, поддерживая счетчик для каждого компонента. На компоненты сохраняются только слабые ссылки, что предотвращает утечку памяти.
Вы говорите, что обход всех элементов не вариант, но мой опыт показывает, что этот код хорошо работает для довольно сложных графических интерфейсов. Между прочим, если бы у 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);
}
}
}
}
}
Если я не ошибаюсь, использование этого класса потребует, чтобы каждый раз, когда кто-то хочет включить / отключить компонент, они использовали этот класс вместо вызова метода setEnabled () компонента. В противном случае этот класс может непреднамеренно включать / отключать компонент, которого он не должен делать, потому что он не знает «родное» состояние дочерних компонентов при вызове одного из методов setMore * ().
Правильно, "разрешенное" состояние компонентов должно полностью контролироваться этими двумя методами.
Однако это возможно только в том случае, если вы хотите только заблокировать взаимодействие с пользователем. Фактически, все программные вызовы, включая вызовы ui-robot (например, doClick), могут по-прежнему проходить и запускать соответствующие события.