如何在Swing中禁用容器及其子代

时间:2008-11-20 14:52:51

标签: java swing

我无法找到一种在Swing中禁用容器及其子代的方法。 Swing真的缺少这个基本功能吗?

如果我在容器上执行setEnabled(false),则其子项仍处于启用状态。

我的GUI结构非常复杂,并且不能选择对容器下面的所有元素进行转换。 GlassPane也不在容器顶部(容器不是整个窗口)。

6 个答案:

答案 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)

根据{{​​3}}:

JXLayer可能正是您所寻找的

  

使用JXLayer包装您的容器并在此之后调用JXLayer.setLocked(true) - 将禁用其中的所有组件

this post

答案 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);
    }
}