Swing má mnoho užitečných tříd, které usnadňují vývoj grafického uživatelského rozhraní (GUI). Některé z těchto tříd však nejsou dobře implementovány. Jedním příkladem takové třídy je Skupina tlačítek
. Tento článek vysvětluje proč Skupina tlačítek
je špatně navržen a nabízí náhradní třídu, JButtonGroup
, který dědí z Skupina tlačítek
a opravuje některé z jeho problémů.
Poznámka: Zdrojový kód tohoto článku si můžete stáhnout ze zdrojů.
Otvory pro knoflíkové skupiny
Tady je běžný scénář ve vývoji Swing GUI: Vytvoříte formulář pro shromažďování dat o položkách, které někdo zadá do databáze nebo uloží do souboru. Formulář může obsahovat textová pole, zaškrtávací políčka, přepínače a další widgety. Používáte Skupina tlačítek
třídy seskupit všechny přepínače, které vyžadují jeden výběr. Když je návrh formuláře připraven, začnete implementovat data formuláře. Setkáte se se sadou přepínačů a potřebujete vědět, které tlačítko ve skupině bylo vybráno, abyste mohli příslušné informace uložit do databáze nebo souboru. Nyní jste uvízl. Proč? The Skupina tlačítek
třída vám nedává odkaz na tlačítko aktuálně vybrané ve skupině.
Skupina tlačítek
má getSelection ()
metoda, která vrací model vybraného tlačítka (jako a ButtonModel
typ), nikoli samotné tlačítko. Nyní by to mohlo být v pořádku, pokud byste mohli získat odkaz na tlačítko z jeho modelu, ale nemůžete. The ButtonModel
rozhraní a jeho implementační třídy vám neumožňují načíst odkaz na tlačítko z jeho modelu. Tak co děláš? Podíváte se na Skupina tlačítek
dokumentaci a viz getActionCommand ()
metoda. Pamatujete si, že pokud vytvoříte instanci a JRadioButton
s Tětiva
pro text zobrazený vedle tlačítka a poté zavoláte getActionCommand ()
na tlačítku se vrátí text v konstruktoru. Možná si myslíte, že můžete v kódu pokračovat, protože i když nemáte odkaz na tlačítko, máte alespoň jeho text a stále znáte vybrané tlačítko.
No, překvapení! Váš kód se za běhu rozbije s NullPointerException
. Proč? Protože getActionCommand ()
v ButtonModel
se vrací nula
. Pokud vsadíte (jako já), že getActionCommand ()
produkuje stejný výsledek, ať už je vyvolán na tlačítku nebo na modelu (což je případ mnoho jiné metody, jako např isSelected ()
, je povoleno()
nebo getMnemonic ()
), prohrál jsi. Pokud výslovně nezavoláte setActionCommand ()
na tlačítku nenastavíte příkaz action v jeho modelu a vrátí se metoda getter nula
pro model. Metoda getru dělá při vyvolání tlačítka vrátí text tlačítka. Zde je getActionCommand ()
metoda v AbstractButton
, zděděné všemi třídami tlačítek v Swingu:
public String getActionCommand () {String ac = getModel (). getActionCommand (); if (ac == null) {ac = getText (); } vrátit ac; }
Tato nekonzistence v nastavení a získávání příkazu akce je nepřijatelná. Této situaci se můžete vyhnout, pokud setText ()
v AbstractButton
nastaví příkaz akce modelu na text tlačítka, když je příkaz akce null. Koneckonců, pokud setActionCommand ()
je výslovně volána s některými Tětiva
argument (ne null), text tlačítka je považováno za akční příkaz samotným tlačítkem. Proč by se model měl chovat jinak?
Když váš kód potřebuje odkaz na aktuálně vybrané tlačítko v Skupina tlačítek
, musíte postupovat podle těchto kroků, z nichž žádný nezahrnuje volání getSelection ()
:
- Volání
getElements ()
naSkupina tlačítek
, který vracíVýčet
- Iterovat skrz
Výčet
získat odkaz na každé tlačítko - Volání
isSelected ()
na každém tlačítku zjistit, zda je vybráno - Vrátí odkaz na tlačítko, které vrátilo true
- Nebo pokud potřebujete příkaz akce, zavolejte
getActionCommand ()
na tlačítku
Pokud to vypadá jako spousta kroků, jen abyste získali odkaz na tlačítko, přečtěte si dále. věřím Skupina tlačítek
Implementace je zásadně špatná. Skupina tlačítek
udržuje odkaz na model vybraného tlačítka, když by měl ve skutečnosti ponechat odkaz na samotné tlačítko. Kromě toho od getSelection ()
načte metodu vybraného tlačítka, možná si myslíte, že je odpovídající metoda setteru setSelection ()
, ale není: je setSelected ()
. Nyní, setSelected ()
má velký problém. Jeho argumenty jsou a ButtonModel
a booleovský. Pokud zavoláte setSelected ()
na Skupina tlačítek
a předat knoflíkový model, který není součástí skupiny a skutečný
jako argumenty bude toto tlačítko vybráno a všechna tlačítka ve skupině budou zrušena. Jinými slovy, Skupina tlačítek
má moc vybrat nebo zrušit výběr libovolného tlačítka předaného jeho metodě, přestože tlačítko nemá nic společného se skupinou. K tomuto chování dochází, protože setSelected ()
v Skupina tlačítek
nekontroluje, zda ButtonModel
reference přijatá jako argument představuje tlačítko ve skupině. A protože metoda vynucuje jeden výběr, ve skutečnosti zruší výběr vlastních tlačítek pro výběr jednoho, který nesouvisí se skupinou.
Toto ustanovení v Skupina tlačítek
dokumentace je ještě zajímavější:
No ne tak úplně. Můžete použít jakékoli tlačítko, sedět kdekoli ve své aplikaci, ať už je viditelné nebo ne, a dokonce i deaktivované. Ano, můžete dokonce použít skupinu tlačítek k výběru deaktivovaného tlačítka mimo skupinu a stále zruší výběr všech jejích tlačítek. Chcete-li získat odkazy na všechna tlačítka ve skupině, musíte zavolat absurdní getElements ()
. S čím mají „prvky“ společného Skupina tlačítek
je někdo hádat. Jméno bylo pravděpodobně inspirováno Výčet
metody třídy (hasMoreElements ()
a nextElement ()
), ale getElements ()
jasně měl být pojmenován getButtons ()
. Skupina tlačítek seskupuje tlačítka, nikoli prvky.
Řešení: JButtonGroup
Ze všech těchto důvodů jsem chtěl implementovat novou třídu, která by opravila chyby Skupina tlačítek
a poskytovat uživateli určité funkce a pohodlí. Musel jsem se rozhodnout, zda by měla být třída novou třídou nebo zdědit Skupina tlačítek
. Všechny předchozí argumenty naznačují vytvoření nové třídy spíše než a Skupina tlačítek
podtřída. Nicméně ButtonModel
rozhraní vyžaduje metodu setGroup ()
to trvá a Skupina tlačítek
argument. Pokud jsem nebyl připraven znovu implementovat i knoflíkové modely, byla moje jediná možnost podtřída Skupina tlačítek
a přepsat většinu svých metod. Když už mluvíme o ButtonModel
rozhraní, všimněte si absence volané metody getGroup ()
.
Jeden další problém, který jsem nezmínil, je ten Skupina tlačítek
interně udržuje odkazy na svá tlačítka v a Vektor
. Zbytečně tedy synchronizuje Vektor
režii, kdy by měla použít ArrayList
, protože třída sama o sobě není bezpečná pro vlákna a Swing je stejně jedno vlákno. Chráněná proměnná tlačítka
je prohlášen za Vektor
typ, a ne Seznam
jak můžete očekávat od dobrého programovacího stylu. Proto jsem nemohl znovu implementovat proměnnou jako ArrayList
; a protože jsem chtěl zavolat super.add ()
a super.odstranit ()
, Nemohl jsem skrýt proměnnou nadtřídy. Takže jsem problém opustil.
Navrhuji třídu JButtonGroup
, tónově s většinou názvů tříd Swing. Třída přepíše většinu metod v Skupina tlačítek
a poskytuje další pohodlné metody. Udržuje odkaz na aktuálně vybrané tlačítko, které můžete načíst jednoduchým voláním getSelected ()
. Díky Skupina tlačítek
špatná implementace, mohl bych pojmenovat svoji metodu getSelected ()
, od té doby getSelection ()
je metoda, která vrací model tlačítka.
Následující jsou JButtonGroup
metody.
Nejprve jsem provedl dvě úpravy přidat()
metoda: Pokud je tlačítko, které se má přidat, již ve skupině, vrátí se metoda. Nelze tedy přidat tlačítko do skupiny více než jednou. S Skupina tlačítek
, můžete vytvořit JRadioButton
a přidejte jej 10krát do skupiny. Povolání getButtonCount ()
pak se vrátí 10. To by se nemělo stát, takže nepovoluji duplicitní odkazy. Pak, pokud bylo přidané tlačítko dříve vybráno, stane se vybraným tlačítkem (toto je výchozí chování v Skupina tlačítek
, což je rozumné, takže jsem to nepřepsal). The vybrané tlačítko
proměnná je odkaz na aktuálně vybrané tlačítko ve skupině:
public void add (tlačítko AbstractButton) buttons.contains (button)) return; super.add (tlačítko); if (getSelection () == button.getModel ()) selectedButton = button;
Přetížený přidat()
metoda přidá do skupiny celou řadu tlačítek. Je užitečné, když uložíte odkazy na tlačítka v poli pro zpracování bloku (tj. Nastavení okrajů, přidání posluchačů akcí atd.):
public void add (AbstractButton [] tlačítka) {if (buttons == null) návrat; pro (int i = 0; i
Následující dvě metody odstraní tlačítko nebo řadu tlačítek ze skupiny:
public void remove (tlačítko AbstractButton) {if (button! = null) {if (selectedButton == button) selectedButton = null; super.odstranit (tlačítko); }} public void remove (AbstractButton [] tlačítka) {if (buttons == null) návrat; pro (int i = 0; i
Dále první setSelected ()
metoda umožňuje nastavit stav výběru tlačítka předáním odkazu na tlačítko namísto jeho modelu. Druhá metoda přepíše odpovídající setSelected ()
v Skupina tlačítek
zajistit, aby skupina mohla vybrat nebo zrušit výběr pouze tlačítka, které patří do skupiny:
public void setSelected (tlačítko AbstractButton, vybráno boolean) {if (button! = null && buttons.contains (button)) {setSelected (button.getModel (), selected); if (getSelection () == button.getModel ()) selectedButton = button; }} public void setSelected (ButtonModel model, boolean selected) {AbstractButton button = getButton (model); if (buttons.contains (button)) super.setSelected (model, selected); }
The getButton ()
metoda načte odkaz na tlačítko, jehož model je uveden. setSelected ()
používá tuto metodu k načtení tlačítka, které má být vybráno vzhledem k jeho modelu. Pokud model předaný metodě patří tlačítku mimo skupinu, nula
je vrácen. Tato metoda by měla existovat v ButtonModel
implementace, ale bohužel ne:
public AbstractButton getButton (model ButtonModel) {Iterator it = buttons.iterator (); while (it.hasNext ()) {AbstractButton ab = (AbstractButton) it.next (); if (ab.getModel () == model) vrátit ab; } return null; }
getSelected ()
a isSelected ()
jsou nejjednodušší a pravděpodobně nejužitečnější metody JButtonGroup
třída. getSelected ()
vrací odkaz na vybrané tlačítko a isSelected ()
přetíží metodu stejného jména v Skupina tlačítek
vzít odkaz na tlačítko:
public AbstractButton getSelected () {return selectedButton; } public boolean isSelected (AbstractButton button) {return button == selectedButton; }
Tato metoda kontroluje, zda je tlačítko součástí skupiny:
public boolean obsahuje (tlačítko AbstractButton) {návrat buttons.contains (tlačítko); }
Očekávali byste metodu s názvem getButtons ()
v Skupina tlačítek
třída. Vrátí neměnný seznam obsahující odkazy na tlačítka ve skupině. Neměnný seznam zabrání přidání nebo odebrání tlačítka, aniž by prošel metodami skupiny tlačítek. getElements ()
v Skupina tlačítek
má nejen zcela neinspirované jméno, ale také vrací Výčet
, což je zastaralá třída, kterou byste neměli používat. Kolekce Framework poskytuje vše, co potřebujete, abyste se vyhnuli výčtu. Takhle getButtons ()
vrací neměnný seznam:
public List getButtons () {návrat Collections.unmodifiableList (tlačítka); }
Vylepšete ButtonGroup
The JButtonGroup
třída nabízí lepší a pohodlnější alternativu k Swingu Skupina tlačítek
třídy, při zachování všech funkcí nadtřídy.
Další informace o tomto tématu
- Stáhněte si zdrojový kód, který je přiložen k tomuto článku
//images.techhive.com/downloads/idge/imported/article/jvw/2003/09/jw-javatip142.zip
- Domovská stránka tříd Java Foundation Classes společnosti Sun Microsystems
//java.sun.com/products/jfc/
- Dokumentace k platformě Java 2 Platform, Standard Edition (J2SE) 1.4.2 API
//java.sun.com/j2se/1.4.2/docs/api/
- Třída ButtonGroup
//java.sun.com/j2se/1.4.2/docs/api/javax/swing/ButtonGroup.html
- Zobrazit všechny předchozí Tipy pro Java a odešlete vlastní
//www.javaworld.com/columns/jw-tips-index.shtml
- Procházejte AWT / Swing část JavaWorld 's Aktuální rejstřík
//www.javaworld.com/channel_content/jw-awt-index.shtml
- Procházejte Třídy nadace část JavaWorld 's Aktuální rejstřík
//www.javaworld.com/channel_content/jw-foundation-index.shtml
- Procházejte Návrh uživatelského rozhraní část JavaWorld 's Aktuální rejstřík
//www.javaworld.com/channel_content/jw-ui-index.shtml
- Navštivte fórum JavaWorld
//www.javaworld.com/javaforums/ubbthreads.php?Cat=&C=2
- Přihlásit se JavaWorld 's týdenní e-mailové zpravodaje zdarma
//www.javaworld.com/subscribe
Tento příběh, „Java Tip 142: Pushing JButtonGroup“, původně publikoval JavaWorld.