java 如何在 Swing 中禁用容器及其子项
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/305527/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to disable a container and its children in Swing
提问by Fuzzy76
I cannot figure out a way to disable a container AND its children in Swing. Is Swing really missing this basic feature?
我想不出在 Swing 中禁用容器及其子项的方法。Swing 真的缺少这个基本功能吗?
If I do setEnabled(false) on a container, its children are still enabled.
如果我在容器上执行 setEnabled(false) ,它的子项仍然处于启用状态。
My GUI structure is pretty complex, and doing a traversion of all elements below the container is not an option. Neither is a GlassPane on top of the container (the container is not the entire window).
我的 GUI 结构非常复杂,无法遍历容器下方的所有元素。容器顶部的 GlassPane 也不是(容器不是整个窗口)。
采纳答案by VonC
To add to mmyers's answer, disabling children is not an easy task (see this thread)
要添加到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)
在一般情况下,这个问题几乎无法解决。这就是为什么它不是核心 Swing 的一部分。
从技术上讲,disable-and-store-old-state 和 enable-and-restore-to-old-state 可能看起来很有吸引力。在特殊情况下,它甚至可能是一个不错的选择。但是有(至少,可能还有更多)两个问题。
复合组件
递归必须在“复合组件”(或“单个实体”)上停止。然后组件负责保持依赖的状态。没有通用的方法来检测这样的组件 - 例如 JComboBox、JXDatePicker(作为相关问题)
为了让事情变得更加复杂,依赖项不需要位于“复合组件”的层次结构下,fi JXTable 负责处理 ColumnControl(和标题)的启用状态。
试图解决这两个问题需要有
a) 大院的财产:“不要碰我的孩子”和
b) 未收容家属的财产:“不要碰我”绑定到启用
如果启用状态绑定到(演示或其他)模型属性并且该属性在此期间更改 - 现在旧状态无效,则 enable-and-update-to-old 可能会破坏应用程序状态。
试图解决这需要有
c) 一个“真实的”stored-old-enabled-due-to-view-concerns 属性
d) 将表示模型属性绑定到启用和存储旧启用JXRadioGroup 有该问题的一个变体:在禁用时 - 组本身或通用控制器 - 跟踪每个按钮的旧启用状态。按钮的启用由动作控制 - 如果有动作。所以启用的控制器需要恢复到旧的启用或动作的启用。在组禁用(作为组)期间,如果操作的启用在存储时为 false 并更改为 true,则会出现问题。另一个 if 动作被添加。
现在想象一下重载 a)-- d) 时状态转换的复杂性
回答by Michael Myers
JXLayermight be what you're looking for, according to this post:
Wrap your container with the JXLayer and call
JXLayer.setLocked(true)after that - all components inside will be disabled
用 JXLayer 包裹你的容器并在之后调用
JXLayer.setLocked(true)- 里面的所有组件都将被禁用
alt text http://www.java.net/download/javadesktop/blogs/alexfromsun/2007.06.25/LayerDemo.PNG
替代文字 http://www.java.net/download/javadesktop/blogs/alexfromsun/2007.06.25/LayerDemo.PNG
回答by barjak
This is the code I use. It recursively visits the component tree, maintaining a counter for each component. Only weak references are kept on the components, preventing any memory leak.
这是我使用的代码。它递归地访问组件树,为每个组件维护一个计数器。组件上只保留弱引用,防止任何内存泄漏。
You say that traversing all the elements is not an option, but my experience is that this code works well for quite complex GUIs. By the way, if Swing had this feature natively, there would be no other way than traversing the component tree, anyway.
你说遍历所有元素不是一个选项,但我的经验是这段代码适用于非常复杂的 GUI。顺便说一句,如果 Swing 本身就有这个特性,那么无论如何,除了遍历组件树之外别无他法。
Example usage (parenthesis means disabled) :
用法示例(括号表示禁用):
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)
Now the code :
现在的代码:
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);
}
}
}
}
}
回答by Dylan
This is what I came up with.
这就是我想出的。
Component[] comps = myPanel.getComponents();
for (Component comp:comps){
comp.setEnabled(false);
}
回答by Defd
As VonC's answer, there's no simple solution existed. So i recommend you to program with a supporting infrastructure from the start.
正如 VonC 的回答,不存在简单的解决方案。因此,我建议您从一开始就使用支持基础架构进行编程。
A simple infrastructure is likely to be, for example, using delegated listeners that do a "event enabled" check from a super container's flag before actual event-respond:
例如,一个简单的基础设施可能是使用委托侦听器,在实际事件响应之前,从超级容器的标志中进行“事件启用”检查:
class ControlledActionListener extends ActionListener {
...
public void actionPerformed( ActionEvent e ) {
if( !container.isEnabled() ) return;
doYourBusinessHere();
}
}
Or even better, you can use the APT to automatically inject the boilerplate code for you.
或者更好的是,您可以使用 APT 自动为您注入样板代码。
This works well all the time. It's the clean way to block both user interaction and programming callswith a single effort. Even though it costs you some codes to support the underlying functionality, you get simplicity, usablity and stability in return.
这一直都很有效。这是一次性阻止用户交互和编程调用的干净方式。尽管您需要花费一些代码来支持底层功能,但您可以获得简单性、可用性和稳定性的回报。
PS. i'd like to see better solution to this problem.
附注。我想看到这个问题的更好解决方案。
回答by spygas
I would suggest you to write a recursive method which finds all the java.awt.Container instances in your java.awt.Container and sets its components enabled/disabled. This is how I solved such a problem in my extended JFrame class:
我建议您编写一个递归方法,该方法在您的 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);
}
}

