我无法找到一种在Swing中禁用容器及其子代的方法。 Swing真的缺少这个基本功能吗?
如果我在容器上执行setEnabled(false),则其子项仍处于启用状态。
我的GUI结构非常复杂,并且不能选择对容器下面的所有元素进行转换。 GlassPane也不在容器顶部(容器不是整个窗口)。
答案 0 :(得分:13)
要添加到mmyers's answer,禁用儿童并非易事(请参阅此thread)
在一般情况下,问题几乎无法解决。这就是为什么它不是核心Swing的一部分。
从技术上讲,禁用和存储旧状态后跟启用和恢复到旧状态可能看起来很有吸引力。在特殊情况下,它甚至可能是一个不错的选择。但是有两个问题(至少可能还有一堆)。
复合组件
递归必须停在“复合组件”(或“单个实体”)上。然后组件负责保持依赖状态。没有通用的方法来检测这样的组件 - 示例是JComboBox,JXDatePicker(与相关的issue)
为了使事情变得更加复杂,依赖者不需要在“复合组件”的层次结构下,f.i。 JXTable负责处理ColumnControl(和标头)的启用状态。
尝试解决这两个问题需要
a)该化合物的财产:“不要触摸我的孩子”和
b)未受约束的家属的财产:“别碰我”绑定到已启用
如果启用状态绑定到(表示或其他)模型属性并且该属性在此期间发生更改,则enable-and-update-to-old可能会破坏应用程序状态 - 现在旧状态无效。 / p>
试图解决这个问题需要
c)“真实的”存储旧的启用的视图关注属性
d)将表示模型属性绑定到启用的和存储的旧启用的JXRadioGroup有一个问题的变体:在禁用时 - 组本身或通用控制器 - 跟踪每个按钮的旧启用。按钮已启用由操作控制 - 如果有操作。因此,启用的控制器需要恢复到启用旧功能或启用操作。在组的禁用(as-group)期间,如果Action的启用在存储时为false并且更改为true,则会出现问题。 添加了另一个动作。
现在想象一下重载a)时的状态转换的复杂性 - d)
答案 1 :(得分:12)
答案 2 :(得分:4)
这是我使用的代码。它以递归方式访问组件树,为每个组件维护一个计数器。只有弱引用保留在组件上,防止任何内存泄漏。
你说遍历所有元素不是一个选项,但我的经验是这个代码适用于非常复杂的GUI。顺便说一句,如果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);
}
}
}
}
}
答案 3 :(得分:0)
这就是我想出来的。
Component[] comps = myPanel.getComponents();
for (Component comp:comps){
comp.setEnabled(false);
}
答案 4 :(得分:0)
作为VonC的回答,没有简单的解决方案。因此,我建议您从一开始就使用支持基础架构进行编程。
一个简单的基础设施可能是,例如,使用委托的侦听器,在实际的事件响应之前从超级容器的标志执行“事件启用”检查:
class ControlledActionListener extends ActionListener {
...
public void actionPerformed( ActionEvent e ) {
if( !container.isEnabled() ) return;
doYourBusinessHere();
}
}
甚至更好,您可以使用APT自动为您注入样板代码。
这一直都很有效。只需一次就能阻止用户交互和编程调用的干净方法。即使您需要一些代码来支持底层功能,您仍然可以获得简单,可用和稳定的回报。
PS。我希望看到更好的解决方案。
答案 5 :(得分:0)
我建议你编写一个递归方法,找到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);
}
}