Programiranje

Java Nasvet 142: potiskanje JButtonGroup

Swing ima veliko uporabnih razredov, ki olajšajo razvoj grafičnega uporabniškega vmesnika (GUI). Nekateri od teh razredov pa niso dobro izvedeni. En primer takega razreda je ButtonGroup. Ta članek pojasnjuje, zakaj ButtonGroup je slabo oblikovan in ponuja nadomestni razred, JButtonGroup, ki podeduje od ButtonGroup in odpravi nekatere težave.

Opomba: Izvorno kodo tega članka lahko prenesete iz virov.

ButtonGroup holes

Tu je pogost scenarij pri razvoju grafičnega uporabniškega vmesnika Swing: Izdelate obrazec za zbiranje podatkov o elementih, ki jih bo nekdo vnesel v bazo podatkov ali shranil v datoteko. Obrazec lahko vsebuje polja z besedilom, potrditvena polja, izbirne gumbe in druge pripomočke. Uporabljate ButtonGroup razred, da združite vse izbirne gumbe, ki potrebujejo eno izbiro. Ko je oblikovanje obrazca pripravljeno, začnete izvajati podatke obrazca. Naletite na nabor izbirnih gumbov in vedeti morate, kateri gumb v skupini je bil izbran, da lahko ustrezne podatke shranite v bazo podatkov ali datoteko. Zdaj ste zaljubljeni. Zakaj? The ButtonGroup razred vam ne daje sklica na gumb, ki je trenutno izbran v skupini.

ButtonGroup ima getSelection () metoda, ki vrne model izbranega gumba (kot ButtonModel tipa), ne samega gumba. Zdaj je to morda v redu, če lahko referenco na gumb dobite iz njegovega modela, vendar ne morete. The ButtonModel vmesnik in njegovi izvedbeni razredi ne omogočajo pridobivanja sklica gumba iz njegovega modela. Torej s čim se ukvarjate? Poglej ButtonGroup dokumentacijo in si oglejte getActionCommand () metoda. Spomnite se, da če ustvarite primer JRadioButton z Vrvica za besedilo, prikazano ob gumbu, in nato pokličete getActionCommand () na gumbu se vrne besedilo v konstruktorju. Morda si mislite, da lahko še vedno nadaljujete s kodo, ker četudi nimate sklica na gumb, imate vsaj njegovo besedilo in še vedno poznate izbrani gumb.

No, presenečenje! Vaša koda se med izvajanjem zlomi z NullPointerException. Zakaj? Ker getActionCommand () v ButtonModel vrne nič. Če stavite (kot sem jaz) to getActionCommand () daje enak rezultat, ne glede na to, ali je poklican na gumbu ali na modelu (kar velja za veliko druge metode, kot so isSelected (), isEnabled (), ali getMnemonic ()), izgubil si. Če izrecno ne pokličete setActionCommand () na gumbu v njegovem modelu ne nastavite ukaza action in metoda getter se vrne nič za model. Vendar metoda pridobivanja naredi vrne besedilo gumba, ko ga pokličete. Tukaj je getActionCommand () metoda v PovzetekButton, ki so ga podedovali vsi razredi gumbov v Swingu:

 javni String getActionCommand () {Niz ac = getModel (). getActionCommand (); if (ac == null) {ac = getText (); } vrnitev ac; } 

Ta nedoslednost pri nastavljanju in pridobivanju ukaza za dejanje je nesprejemljiva. Tej situaciji se lahko izognete, če setText () v PovzetekButton nastavi ukaz dejanja modela na besedilo gumba, ko je ukaz dejanja ničen. Konec koncev, razen če setActionCommand () se pri nekaterih imenuje izrecno Vrvica argument (ne null), besedilo gumba je šteje za ukaz dejanja s samim gumbom. Zakaj bi se model moral obnašati drugače?

Ko vaša koda potrebuje sklic na trenutno izbrani gumb v ButtonGroup, morate slediti tem korakom, med katerimi noben ne vključuje klicanja getSelection ():

  • Pokliči getElements () na ButtonGroup, ki vrne Naštevanje
  • Ponavljajte skozi Naštevanje da dobite sklic na posamezen gumb
  • Pokliči isSelected () na vsakem gumbu, da ugotovite, ali je izbran
  • Vrnite sklic na gumb, ki je vrnil true
  • Če potrebujete ukaz za dejanje, pokličite getActionCommand () na gumbu

Če je to videti kot veliko korakov samo za sklic gumba, preberite. verjamem ButtonGroupIzvajanje je v osnovi napačno. ButtonGroup ohrani sklic na model izbranega gumba, kadar naj dejansko hrani sklic na sam gumb. Poleg tega od getSelection () pridobi metodo izbranega gumba, morda mislite, da je ustrezna metoda nastavitve setSelection (), ampak ni: je setSelected (). Zdaj, setSelected () ima velik problem. Njeni argumenti so a ButtonModel in logično vrednost. Če pokličete setSelected () na a ButtonGroup in podajte model gumba, ki ni del skupine in prav kot argumente, potem ta gumb postane izbran in vsi gumbi v skupini postanejo neizbrani. Z drugimi besedami, ButtonGroup ima možnost, da izbere ali prekliče izbiro katerega koli gumba, ki je bil poslan njegovi metodi, čeprav ta nima nič skupnega s skupino. Do tega vedenja pride, ker setSelected () v ButtonGroup ne preveri, ali je ButtonModel sklic, prejet kot argument, predstavlja gumb v skupini. In ker metoda uveljavi en izbor, dejansko prekliče izbiro lastnih gumbov, da izbere enega, ki ni povezan s skupino.

Ta določba v ButtonGroup dokumentacija je še bolj zanimiva:

Za brisanje skupine gumbov ni mogoče programsko obrniti gumba v položaj 'off'. Če želite videti, da ni izbran, dodajte skupini neviden izbirni gumb in nato programsko izberite ta gumb, da izklopite vse prikazane izbirne gumbe. Za izbiro nevidnega izbirnega gumba lahko na primer povežete običajni gumb z oznako „nič“.

No, pravzaprav ne. Uporabite lahko kateri koli gumb, ki sedi kjer koli v vaši aplikaciji, viden ali ne, in celo onemogočen. Da, lahko celo uporabite skupino gumbov, da izberete onemogočen gumb zunaj skupine, vendar bo še vedno preklical izbor vseh gumbov. Če se želite sklicevati na vse gumbe v skupini, morate poklicati smešno getElements (). Kakšne povezave imajo "elementi" ButtonGroup je kdo uganil. Ime je verjetno navdihnilo Naštevanje metode razreda (hasMoreElements () in nextElement ()), vendar getElements () očitno bi moral biti imenovan getButtons (). Skupina gumbov združuje gumbe in ne elemente.

Rešitev: JButtonGroup

Zaradi vseh teh razlogov sem hotel uvesti nov razred, ki bo odpravil napake v ButtonGroup in uporabniku nudijo nekaj funkcionalnosti in udobja. Moral sem se odločiti, ali naj bo razred nov ali ga podedujem ButtonGroup. Vsi prejšnji argumenti predlagajo ustvarjanje novega razreda namesto a ButtonGroup podrazred. Vendar pa ButtonModel vmesnik zahteva metodo setGroup () ki traja a ButtonGroup prepir. Če nisem bil pripravljen ponovno uporabiti tudi modelov gumbov, je bila moja edina možnost podrazred ButtonGroup in preglasi večino svojih metod. Ko smo že pri ButtonModel vmesnik, opazite odsotnost imenovane metode getGroup ().

Še eno vprašanje, ki ga nisem omenil, je to ButtonGroup interno hrani sklice na svoje gumbe v a Vektor. Tako po nepotrebnem sinhronizira Vektorrežijske stroške, ko bi moral uporabiti ArrayList, ker razred sam po sebi ni varen in je Swing vseeno enojni navoj. Vendar zaščitena spremenljivka gumbi je razglašen za Vektor tipa in ne Seznam kot bi lahko pričakovali dober slog programiranja. Tako spremenljivke nisem mogel ponovno uporabiti kot ArrayList; in ker sem hotel poklicati super.add () in super.remove (), Spremenljivke superklase nisem mogel skriti. Zato sem vprašanje opustil.

Predlagam razred JButtonGroup, v tonu z večino imen razreda Swing. Razred preglasi večino metod v ButtonGroup in nudi dodatne metode priročnosti. Shrani sklic na trenutno izbrani gumb, ki ga lahko pridobite s preprostim klicem getSelected (). Zahvale gredo ButtonGroupje slaba izvedba, lahko bi poimenoval svojo metodo getSelected (), od getSelection () je metoda, ki vrne model gumba.

Sledijo JButtonGroupmetode.

Najprej sem naredil dve modifikaciji dodaj () metoda: Če je gumb, ki ga želite dodati, že v skupini, se metoda vrne. Tako skupini ne morete dodati gumba več kot enkrat. S ButtonGroup, lahko ustvarite JRadioButton in ga dodajte 10-krat v skupino. Klicanje getButtonCount () nato vrne 10. To se ne bi smelo zgoditi, zato ne dovolim podvojenih referenc. Če je bil dodani gumb že izbran, postane izbrani gumb (to je privzeto vedenje v ButtonGroup, kar je smiselno, zato ga nisem preglasil). The selectedButton spremenljivka je sklic na trenutno izbrani gumb v skupini:

javni void add (gumb AbstractButton) gumbi. vsebuje (gumb)) return; super.add (gumb); če je (getSelection () == button.getModel ()) selectedButton = button; 

Preobremenjeni dodaj () metoda v skupino doda celo vrsto gumbov. Uporabno je, če sklice gumbov shranite v matriko za obdelavo blokov (tj. Nastavitev meja, dodajanje poslušalcev dejanj itd.):

javni void add (gumbi AbstractButton []) {if (gumbi == null) return; za (int i = 0; i

Naslednja dva načina odstranijo gumb ali vrsto gumbov iz skupine:

javna praznina odstranite (gumb AbstractButton) {if (gumb! = null) {if (izbrani gumb == gumb) selectedButton = null; super.remove (gumb); }} javna void odstranitev (gumbi AbstractButton []) {if (gumbi == null) return; za (int i = 0; i

V nadaljevanju prvi setSelected () metoda vam omogoča, da nastavite stanje izbire gumba tako, da namesto modela prenesete referenco gumba. Druga metoda preglasi ustrezno setSelected () v ButtonGroup da zagotovite, da lahko skupina izbere ali prekliče izbor gumba, ki pripada skupini:

javna void setSelected (gumb AbstractButton, izbrano logično ime) {if (gumb! = niče && gumbi.se nahaja (gumb)) {setSelected (button.getModel (), izbrano); če je (getSelection () == button.getModel ()) selectedButton = button; }} javna void setSelected (model ButtonModel, izbrana logična vrednost) {AbstractButton button = getButton (model); if (buttons.contens (button)) super.setSelected (model, izbran); } 

The getButton () metoda pridobi referenco na gumb, katerega model je podan. setSelected () uporablja to metodo za pridobivanje gumba, ki je izbran glede na njegov model. Če model, ki je poslan metodi, pripada gumbu zunaj skupine, nič se vrne. Ta metoda bi morala obstajati v ButtonModel izvajanja, žal pa ne:

public AbstractButton getButton (model ButtonModel) {Iterator it = buttons.iterator (); medtem ko (it.hasNext ()) {AbstractButton ab = (AbstractButton) it.next (); če (ab.getModel () == model) vrne ab; } vrni null; } 

getSelected () in isSelected () so najpreprostejše in verjetno najbolj uporabne metode JButtonGroup razred. getSelected () vrne sklic na izbrani gumb in isSelected () preobremeni istoimensko metodo v ButtonGroup za sklic gumba:

public AbstractButton getSelected () {return selectedButton; } javno boolean isSelected (gumb AbstractButton) {gumb za vrnitev == selectedButton; } 

Ta metoda preveri, ali je gumb del skupine:

javna logična vrednost vsebuje (gumb AbstractButton) {vrni gumbe. vsebuje (gumb); } 

Pričakovali bi imenovano metodo getButtons () v ButtonGroup razred. Vrne nespremenljiv seznam, ki vsebuje sklice na gumbe v skupini. Nespremenljiv seznam preprečuje dodajanje ali odstranjevanje gumbov, ne da bi šel skozi metode skupine gumbov. getElements () v ButtonGroup ne samo, da ima popolnoma nenadihnjeno ime, ampak vrne tudi Naštevanje, ki je zastarel razred, ki ga ne bi smeli uporabljati. Okvir zbirk ponuja vse, kar potrebujete, da se izognete naštevanju. Takole je getButtons () vrne nespremenljiv seznam:

javni seznam getButtons () {return Collections.unmodifiableList (gumbi); } 

Izboljšajte ButtonGroup

The JButtonGroup razred ponuja boljšo in priročnejšo alternativo gugalnici ButtonGroup razreda, hkrati pa ohranil vso funkcionalnost superrazreda.

Daniel Tofan je podoktorski sodelavec na oddelku za kemijo na Državni univerzi v New Yorku Stony Brook. Njegovo delo vključuje razvoj osrednjega dela sistema vodenja tečajev z uporabo v kemiji. Je pooblaščeni programer za zaščito pred soncem za platformo Java 2 in doktor kemijske znanosti.

Preberite več o tej temi

  • Prenesite izvorno kodo, ki spremlja ta članek

    //images.techhive.com/downloads/idge/imported/article/jvw/2003/09/jw-javatip142.zip

  • Domača stran tečajev Java Foundation Classa podjetja Sun Microsystems

    //java.sun.com/products/jfc/

  • Java 2 Platform, Standard Edition (J2SE) 1.4.2 API dokumentacija

    //java.sun.com/j2se/1.4.2/docs/api/

  • Razred ButtonGroup

    //java.sun.com/j2se/1.4.2/docs/api/javax/swing/ButtonGroup.html

  • Oglejte si vse prejšnje Java Nasveti in oddajte svojega

    //www.javaworld.com/columns/jw-tips-index.shtml

  • Prebrskajte AWT / nihanje odsek JavaWorld 's Aktualni indeks

    //www.javaworld.com/channel_content/jw-awt-index.shtml

  • Prebrskajte Temeljni razredi odsek JavaWorld 's Aktualni indeks

    //www.javaworld.com/channel_content/jw-foundation-index.shtml

  • Prebrskajte Oblika uporabniškega vmesnika odsek JavaWorld 's Aktualni indeks

    //www.javaworld.com/channel_content/jw-ui-index.shtml

  • Obiščite forum JavaWorld

    //www.javaworld.com/javaforums/ubbthreads.php?Cat=&C=2

  • Prijavite se za JavaWorld 'brezplačna tedenska glasila po e-pošti

    //www.javaworld.com/subscribe

To zgodbo "Java Nasvet 142: potiskanje JButtonGroup" je prvotno objavil JavaWorld.

$config[zx-auto] not found$config[zx-overlay] not found