2008-11-20 9 views
16

Nie mogę znaleźć sposobu na wyłączenie kontenera i jego dzieci w Swing. Czy Swing naprawdę brakuje tej podstawowej funkcji?Jak wyłączyć kontener i jego dzieci w Swing?

Jeśli ustawię setEnabled (false) w kontenerze, jego dzieci są nadal włączone.

Moja struktura GUI jest dość złożona, a wykonanie traversal wszystkich elementów pod kontenerem nie jest opcją. Nie ma też GlassPane na górze kontenera (kontener nie jest całym oknem).

Odpowiedz

13

Aby dodać do mmyers's answer, wyłączenie dzieci nie jest zadaniem łatwym (patrz: thread)

Problem jest prawie nie do rozwiązania w ogólnym przypadku. Dlatego nie jest częścią rdzenia Swing.

Z technicznego punktu widzenia stan "wyłącz i przechowuj", po którym następuje "włącz i przywróć do stanu starego", może wyglądać atrakcyjnie. Nawet w szczególnych przypadkach może to być miłe. Ale są (przynajmniej pewnie jeszcze więcej) dwie kwestie z tym związane.

składniki mieszanki

rekursję musi zatrzymać się na „składnik związek” (lub „jednolita”). Następnie składnik odpowiedzialny jest za utrzymanie stanu zależności. Nie ma ogólnego sposobu na wykrycie takiego komponentu - przykłady to JComboBox, JXDatePicker (który jest powiązany z issue)

Aby uczynić rzeczy jeszcze bardziej skomplikowanymi, zależności nie muszą znajdować się w hierarchii "składnika złożonego", np. JXTable dba o stan włączonego ColumnControl (i nagłówka).

próbują rozwiązać zarówno wymagałoby mieć

a) nieruchomość na związek: „Nie dotykaj moich dzieci” i
b) nieruchomości na uncontained utrzymaniu: „Nie dotykaj mnie "

wiązania do włączona

enable-i-update-do-stary może złamać stanu aplikacji, jeśli włączona stan jest związany z prezentacją (lub inny) własności modelu i że własność zmieniło w-the- tymczasem - teraz stary stan jest nieważny.

Próba rozwiązania, które wymagają, aby mieć

c) "prawdziwy" przechowywane-old-enabled-due-to-view-obaw własności
d) powiązać właściwości modelu prezentacji zarówno włączona i przechowywana w starym stanie

JXRadioGroup ma wariant tego problemu: Po wyłączeniu - sama grupa lub kontroler ogólny - śledzi starą funkcję każdego przycisku. Włączenie przycisku jest kontrolowane przez akcję - jeśli jest Akcja. Tak więc włączony kontroler musi przywrócić starą aktywację lub włączyć działanie. Podczas wyłączania grupy (as-group) pojawia się problem, jeśli opcja Akcja została ustawiona na false i została zmieniona na true. Kolejne, jeśli czynności zostały dodane.

Teraz wyobraź sobie złożoność przejścia państwowych przy przeciążeniu a) - d)

12

JXLayer może być to, czego szukasz, według this post:

Owiń pojemnik z JXLayer i nazywają JXLayer.setLocked(true) po tym - wszystkie elementy wewnątrz zostaną wyłączone

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

+0

dobrą alternatywą. +1 – VonC

+1

Jest to jednak możliwe tylko wtedy, gdy chcesz blokować interakcje użytkownika. W rzeczywistości wszystkie wywołania programistyczne, w tym wywołania ui-robot (takie jak doClick), mogą nadal przechodzić przez odpowiednie zdarzenia. – Defd

0

To co wymyśliłem.

Component[] comps = myPanel.getComponents(); 
for (Component comp:comps){ 
    comp.setEnabled(false); 
} 
0

Jako odpowiedź VonC, nie istniało proste rozwiązanie. Polecam więc programowanie z infrastrukturą pomocniczą od samego początku.

Prosty infrastruktura może być, na przykład, za pomocą słuchaczy delegowanych że zrobić „imprezę włączoną” check z flagą super kontenera przed imprezą-Odpowiedz:

class ControlledActionListener extends ActionListener { 
    ... 
    public void actionPerformed(ActionEvent e) { 
     if(!container.isEnabled()) return; 

     doYourBusinessHere(); 
    } 
} 

albo jeszcze lepiej, można użyj APT, aby automatycznie wstrzyknąć Ci kod na płycie głównej.

To działa dobrze przez cały czas. To czysty sposób blokowania zarówno interakcji użytkownika, jak i programowania połączeń przy jednym wysiłku. Mimo że kosztuje kilka kodów, aby wesprzeć podstawową funkcjonalność, otrzymasz w zamian prostotę, funkcjonalność i stabilność.

PS. Chciałbym zobaczyć lepsze rozwiązanie tego problemu.

0

Proponuję napisać metodę rekursywną, która znajdzie wszystkie instancje java.awt.Container w pliku java.awt.Container i włącza/wyłącza jej komponenty. W ten sposób mogę rozwiązać taki problem w moim rozszerzonego klasy 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); 
    } 
} 
4

Jest to kod używam. Rekursywnie odwiedza drzewo komponentów, zachowując licznik dla każdego komponentu. Tylko słabe referencje są przechowywane na komponentach, zapobiegając wszelkim wyciekom pamięci.

Mówisz, że przechodzenie przez wszystkie elementy nie jest opcją, ale moje doświadczenie jest takie, że ten kod działa dobrze dla dość złożonych GUI. Nawiasem mówiąc, gdyby Swing miał tę funkcję natywnie, nie byłoby innego sposobu niż przetoczenie drzewa komponentów.

Przykład użycia (nawias oznacza wyłączony):

 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) 

Teraz kod:

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

} 
+0

Jeśli się nie mylę, użycie tej klasy wymagałoby, aby w każdej chwili włączyć/wyłączyć komponent, używają tej klasy vs wywołując metodę setEnabled() komponentu. Jeśli nie, to ta klasa może niechcący włączyć/wyłączyć komponent, który nie powinien, ponieważ nie zna "natywnego" stanu komponentów potomnych, gdy wywoływana jest jedna z metod setMore *(). – ewh

+0

To prawda, stan "enable" komponentów musi być w pełni kontrolowany za pomocą tych dwóch metod. – barjak

Powiązane problemy