How to disable a container and its children in Swi

2020-02-03 04:43发布

I cannot figure out a way to disable a container AND its children in Swing. Is Swing really missing this basic feature?

If I do setEnabled(false) on a container, its children are still enabled.

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).

标签: java swing
6条回答
Melony?
2楼-- · 2020-02-03 04:44

As VonC's answer, there's no simple solution existed. So i recommend you to program with a supporting infrastructure from the start.

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.

This works well all the time. It's the clean way to block both user interaction and programming calls with 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.

查看更多
我命由我不由天
3楼-- · 2020-02-03 04:46

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:

@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);
    }
}
查看更多
姐就是有狂的资本
4楼-- · 2020-02-03 04:55

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.

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

}
查看更多
太酷不给撩
5楼-- · 2020-02-03 04:57

JXLayer might 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

alt text http://www.java.net/download/javadesktop/blogs/alexfromsun/2007.06.25/LayerDemo.PNG

查看更多
家丑人穷心不美
6楼-- · 2020-02-03 05:07

This is what I came up with.

Component[] comps = myPanel.getComponents();
for (Component comp:comps){
    comp.setEnabled(false);
}
查看更多
劳资没心,怎么记你
7楼-- · 2020-02-03 05:09

To add to mmyers's answer, disabling children is not an easy task (see this thread)

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-enabled

JXRadioGroup 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)

查看更多
登录 后发表回答