Swingissä on monia hyödyllisiä luokkia, jotka tekevät graafisen käyttöliittymän (GUI) kehittämisestä helppoa. Joitakin näistä luokista ei kuitenkaan toteuteta hyvin. Yksi esimerkki tällaisesta luokasta on PainikeRyhmä
. Tässä artikkelissa selitetään miksi PainikeRyhmä
on huonosti suunniteltu ja tarjoaa korvaavan luokan, JPainikeRyhmä
, joka perii PainikeRyhmä
ja korjaa joitain sen ongelmia.
merkintä: Voit ladata tämän artikkelin lähdekoodin Resursseista.
Ryhmäreiät
Tässä on yleinen skenaario Swing-käyttöliittymäkehityksessä: Rakennat lomakkeen kerätäksesi tietoja kohteista, jotka joku lisää tietokantaan tai tallentaa tiedostoon. Lomake voi sisältää tekstiruutuja, valintaruutuja, valintanappeja ja muita widgetejä. Käytät PainikeRyhmä
luokka ryhmittää kaikki valintanapit, jotka tarvitsevat yhden valinnan. Kun lomakkeen suunnittelu on valmis, aloitat lomaketietojen käyttöönoton. Kohdat valintanappipaketin ja sinun on tiedettävä, mikä ryhmän painike on valittu, jotta voit tallentaa tarvittavat tiedot tietokantaan tai tiedostoon. Olet nyt jumissa. Miksi? PainikeRyhmä
luokka ei anna sinulle viitteitä ryhmässä valitulle painikkeelle.
PainikeRyhmä
on getSelection ()
menetelmä, joka palauttaa valitun painikkeen mallin (kuten PainikeMalli
tyyppi), ei itse painiketta. Nyt tämä voi olla kunnossa, jos saat painikeviitteen sen mallista, mutta et voi. PainikeMalli
käyttöliittymä ja sen toteutusluokat eivät salli noutaa painikeviitettä sen mallista. Joten mitä sinä teet? Katsot PainikeRyhmä
asiakirjat ja katso getActionCommand ()
menetelmä. Muistat, että jos aloitat a JRadioPainike
kanssa Merkkijono
painikkeen vieressä näkyvälle tekstille, ja sitten soitat getActionCommand ()
painikkeessa konstruktorin teksti palaa. Saatat ajatella, että voit silti jatkaa koodia, koska vaikka sinulla ei olisi paineviittausta, sinulla on ainakin sen teksti ja tiedät silti valitun painikkeen.
No, yllätys! Koodisi rikkoutuu ajon aikana a: lla NullPointerException
. Miksi? Koska getActionCommand ()
sisään PainikeMalli
palaa tyhjä
. Jos lyöt vetoa (kuten minä) niin getActionCommand ()
tuottaa saman tuloksen riippumatta siitä, kutsutaanko painiketta tai mallia (kuten on monet muut menetelmät, kuten isSelected ()
, on käytössä()
tai getMnemonic ()
), hävisit. Jos et nimenomaisesti soita setActionCommand ()
painikkeessa, et aseta toimintokomentoa sen malliin, ja getter-menetelmä palaa tyhjä
mallille. Kuitenkin getter-menetelmä tekee palauta painikkeen teksti, kun sitä painetaan. Tässä on getActionCommand ()
menetelmä TiivistelmäNappi
, peri kaikki Swingin painikeluokat:
public String getActionCommand () {String ac = getModel (). getActionCommand (); if (ac == null) {ac = getText (); } paluu ac; }
Tätä epäjohdonmukaisuutta toimintokomennon asettamisessa ja saamisessa ei voida hyväksyä. Voit välttää tämän tilanteen, jos setText ()
sisään TiivistelmäNappi
asettaa mallin toimintokomennon painikkeen tekstiksi, kun toimintokomento on tyhjä. Loppujen lopuksi, ellei setActionCommand ()
kutsutaan nimenomaisesti joidenkin kanssa Merkkijono
argumentti (ei nolla), painikkeen teksti On pitää toimintokomentoa itse painikkeella. Miksi mallin pitäisi toimia toisin?
Kun koodisi tarvitsee viitteen tällä hetkellä valitulle painikkeelle PainikeRyhmä
, sinun on noudatettava näitä vaiheita, joista mikään ei sisällä soittamista getSelection ()
:
- Puhelu
getElements ()
päälläPainikeRyhmä
, joka palauttaa arvonLuettelointi
- Toista läpi
Luettelointi
saadaksesi viittauksen jokaiseen painikkeeseen - Puhelu
isSelected ()
kunkin painikkeen kohdalla sen määrittämiseksi, onko se valittu - Palauta viite painikkeeseen, joka palasi tosi
- Tai jos tarvitset toimintokomennon, soita
getActionCommand ()
painiketta
Jos tämä näyttää paljon vaiheilta vain napin viitteen saamiseksi, lue se. minä uskon PainikeRyhmä
Toteutus on pohjimmiltaan väärä. PainikeRyhmä
säilyttää viittauksen valitun painikkeen malliin, kun sen pitäisi itse asiassa pitää viittaus itse painikkeeseen. Lisäksi, koska getSelection ()
noutaa valitun painikkeen menetelmän, saatat ajatella vastaavan setterimenetelmän olevan setSelection ()
, mutta se ei ole: se on setSelected ()
. Nyt, setSelected ()
on iso ongelma. Sen perustelut ovat a PainikeMalli
ja totuusarvo. Jos soitat setSelected ()
a PainikeRyhmä
ja välitä painikkeen malli, joka ei kuulu ryhmään ja totta
argumentteina kyseinen painike valitaan ja kaikkia ryhmän painikkeita ei valita. Toisin sanoen, PainikeRyhmä
on valta valita tai poistaa minkä tahansa menetelmänsä välittämä painike, vaikka painikkeella ei ole mitään tekemistä ryhmän kanssa. Tämä käyttäytyminen tapahtuu, koska setSelected ()
sisään PainikeRyhmä
ei tarkista onko PainikeMalli
argumenttina vastaanotettu viite edustaa ryhmän painiketta. Ja koska menetelmä pakottaa yhden valinnan, se itse poistaa omien painikkeidensa valinnan valitakseen ryhmän, joka ei liity ryhmään.
Tämä määräys PainikeRyhmä
dokumentaatio on vieläkin mielenkiintoisempi:
No eipä oikeastaan. Voit käyttää mitä tahansa painiketta, istuen missä tahansa sovelluksessasi, näkyvissä tai ei, ja jopa pois käytöstä. Kyllä, voit jopa painikeryhmän avulla valita käytöstä poistetun painikkeen ryhmän ulkopuolelta, ja se silti poistaa kaikkien painikkeiden valinnan. Saadaksesi viitteitä ryhmän kaikkiin painikkeisiin, sinun on soitettava naurettavaksi getElements ()
. Mitä "elementteillä" on tekemistä PainikeRyhmä
on kenenkään arvaus. Nimi on todennäköisesti saanut inspiraationsa Luettelointi
luokan menetelmät (hasMoreElements ()
ja seuraavaElement ()
), mutta getElements ()
olisi selvästi pitänyt nimetä getButtons ()
. Painike ryhmittelee painikkeet, ei elementtejä.
Ratkaisu: JButtonGroup
Kaikista näistä syistä halusin ottaa käyttöön uuden luokan, joka korjaa virheet PainikeRyhmä
ja tarjota käyttäjälle jonkin verran toiminnallisuutta ja mukavuutta. Minun piti päättää, pitäisikö luokan olla uusi vai periäkö se PainikeRyhmä
. Kaikki edelliset argumentit viittaavat uuden luokan luomiseen a: n sijaan PainikeRyhmä
alaluokka. Kuitenkin PainikeMalli
käyttöliittymä vaatii menetelmän setGroup ()
joka vie a PainikeRyhmä
Perustelu. Ellei olin valmis uudistamaan myös painikemalleja, ainoa vaihtoehto oli alaluokka PainikeRyhmä
ja ohittaa suurimman osan sen menetelmistä. Puhuminen PainikeMalli
käyttöliittymässä, huomaa kutsutun menetelmän puuttuminen getGroup ()
.
Yksi asia, jota en ole maininnut, on se PainikeRyhmä
pitää sisäisesti viitteitä painikkeisiinsa a Vektori
. Siten synkronoidaan tarpeettomasti Vektori
, kun sen pitäisi käyttää ArrayList
, koska luokka itsessään ei ole lanka turvallinen ja Swing on joka tapauksessa yksisäikeinen. Suojattu muuttuja painikkeita
ilmoitetaan a Vektori
tyyppi, eikä Lista
kuten voit odottaa hyvältä ohjelmointityyliltä. Siksi en voinut lisätä muuttujaa uudelleen ArrayList
; ja koska halusin soittaa super.add ()
ja super.remove ()
, En voinut piilottaa yliluokan muuttujaa. Joten hylkäsin asian.
Ehdotan luokkaa JPainikeRyhmä
, samaan aikaan useimpien Swing-luokkien nimien kanssa. Luokka ohittaa useimmat menetelmät PainikeRyhmä
ja tarjoaa muita mukavuusmenetelmiä. Siinä on viittaus valittuun painikkeeseen, jonka voit hakea yksinkertaisella kutsulla getSelected ()
. Kiitokset PainikeRyhmä
huono toteutus, voisin nimetä menetelmän getSelected ()
, siitä asti kun getSelection ()
on menetelmä, joka palauttaa painikemallin.
Seuraavat ovat JPainikeRyhmä
menetelmät.
Ensinnäkin tein kaksi muunnosta lisätä()
method: Jos lisättävä painike on jo ryhmässä, menetelmä palaa. Siksi et voi lisätä nappia ryhmään useammin kuin kerran. Kanssa PainikeRyhmä
, voit luoda JRadioPainike
ja lisää se 10 kertaa ryhmään. Kutsumus getButtonCount ()
palaa sitten 10. Tämän ei pitäisi tapahtua, joten en salli päällekkäisiä viitteitä. Sitten, jos lisätty painike on aiemmin valittu, siitä tulee valittu painike (tämä on oletusarvoinen toiminto PainikeRyhmä
, mikä on kohtuullista, joten en ohittanut sitä). valittu painike
muuttuja on viittaus ryhmän tällä hetkellä valittuun painikkeeseen:
public void add (AbstractButton button) -painikkeet. sisältää (painike)) return; super.add (painike); jos (getSelection () == button.getModel ()) selectedButton = painike;
Ylikuormitettu lisätä()
method lisää koko joukon painikkeita ryhmään. Se on hyödyllinen, kun tallennat painikkeita viitteitä taulukkoon lohkojen käsittelyä varten (eli rajojen asettaminen, toimintakuuntelijoiden lisääminen jne.):
public void add (AbstractButton [] -painikkeet) {if (painikkeet == null) return; varten (int i = 0; i
Seuraavat kaksi tapaa poistaa painikkeen tai painikeryhmän ryhmästä:
public void remove (AbstractButton button) {if (button! = null) {if (selectedButton == button) selectedButton = null; super.remove (painike); }} public void poista (AbstractButton [] -painikkeet) {if (painikkeet == null) return; varten (int i = 0; i
Tämän jälkeen ensimmäinen setSelected ()
menetelmä antaa sinun asettaa painikkeen valintatilan välittämällä painikkeen viitteen sen mallin sijaan. Toinen menetelmä ohittaa vastaavan setSelected ()
sisään PainikeRyhmä
varmistaa, että ryhmä voi valita tai poistaa vain ryhmään kuuluvan painikkeen:
public void setSelected (AbstractButton button, boolean selected) {if (button! = null && button.contains (button)) {setSelected (button.getModel (), selected); jos (getSelection () == button.getModel ()) selectedButton = painike; }} public void setSelected (ButtonModel-malli, totuusarvo valittu) {AbstractButton-painike = getButton (malli); jos (painikkeet sisältää (painike)) super.setSelected (malli, valittu); }
getButton ()
method hakee viittauksen painikkeeseen, jonka malli on annettu. setSelected ()
käyttää tätä tapaa hakea valitun painikkeen mallinsa perusteella. Jos menetelmälle välitetty malli kuuluu ryhmän ulkopuolella olevaan painikkeeseen, tyhjä
palautetaan. Tämän menetelmän pitäisi olla olemassa PainikeMalli
toteutuksia, mutta valitettavasti se ei:
public AbstractButton getButton (ButtonModel-malli) {Iterator it = painikkeet.iterator (); while (it.hasNext ()) {AbstractButton ab = (AbstractButton) it.next (); if (ab.getModel () == malli) palauttaa ab; } return null; }
getSelected ()
ja isSelected ()
ovat yksinkertaisia ja todennäköisesti hyödyllisimpiä menetelmiä JPainikeRyhmä
luokassa. getSelected ()
palauttaa viitteen valittuun painikkeeseen ja isSelected ()
ylikuormittaa saman nimisen menetelmän sisään PainikeRyhmä
napin viite:
public AbstractButton getSelected () {return selectedButton; } public boolean isSelected (AbstractButton button) {paluupainike == valittuButton; }
Tämä menetelmä tarkistaa, kuuluuko painike ryhmään:
julkinen totuusarvo sisältää (AbstractButton-painike) {paluupainikkeet. sisältää (painike); }
Voit odottaa nimettyä menetelmää getButtons ()
jonkin sisällä PainikeRyhmä
luokassa. Se palauttaa muuttumattoman luettelon, joka sisältää viitteet ryhmän painikkeisiin. Muuttamaton luettelo estää painikkeiden lisäämisen tai poistamisen käymättä läpi painikeryhmän menetelmiä. getElements ()
sisään PainikeRyhmä
ei vain ole täysin inspiroimatonta nimeä, mutta se palauttaa myös Luettelointi
, joka on vanhentunut luokka, jota sinun ei pitäisi käyttää. Kokoelmakehys tarjoaa kaiken mitä tarvitset luetteloiden välttämiseksi. Näin getButtons ()
palauttaa muuttumattoman luettelon:
public List getButtons () {return Collections.unmodifiableList (painikkeet); }
Paranna ButtonGroupia
JPainikeRyhmä
luokka tarjoaa paremman ja mukavamman vaihtoehdon Swingille PainikeRyhmä
luokassa samalla kun säilytetään kaikki superluokan toiminnot.
Lisätietoja tästä aiheesta
- Lataa tämän artikkelin mukana oleva lähdekoodi
//images.techhive.com/downloads/idge/imported/article/jvw/2003/09/jw-javatip142.zip
- Sun Microsystemsin Java Foundation Classes -sivusto
//java.sun.com/products/jfc/
- Java 2 Platform, Standard Edition (J2SE) 1.4.2 -sovellusliittymän dokumentaatio
//java.sun.com/j2se/1.4.2/docs/api/
- ButtonGroup-luokka
//java.sun.com/j2se/1.4.2/docs/api/javax/swing/ButtonGroup.html
- Näytä kaikki edelliset Java-vinkkejä ja lähetä oma
//www.javaworld.com/columns/jw-tips-index.shtml
- Selaa AWT / Swing osa JavaWorld 's Ajankohtainen hakemisto
//www.javaworld.com/channel_content/jw-awt-index.shtml
- Selaa Säätiön luokat osa JavaWorld 's Ajankohtainen hakemisto
//www.javaworld.com/channel_content/jw-foundation-index.shtml
- Selaa Käyttöliittymän suunnittelu osa JavaWorld 's Ajankohtainen hakemisto
//www.javaworld.com/channel_content/jw-ui-index.shtml
- Käy JavaWorld-foorumilla
//www.javaworld.com/javaforums/ubbthreads.php?Cat=&C=2
- Ilmottautua JavaWorld 'Ilmaiset viikoittaiset sähköpostiuutiskirjeet
//www.javaworld.com/subscribe
Tämän tarinan, "Java Tip 142: Pushing JButtonGroup", julkaisi alun perin JavaWorld.