Ohjelmointi

Java-vinkki 35: Luo uusia tapahtumatyyppejä Java-sovelluksessa

Vaikka JDK 1.1 on varmasti virtaviivaistanut tapahtumien käsittelyä ottamalla käyttöön valtuuskunnan tapahtumamallin, se ei tee kehittäjille helppoa luoda omia tapahtumistyyppejä. Tässä kuvattu perusmenettely on itse asiassa melko yksinkertainen. Yksinkertaisuuden vuoksi en käsittele tapahtumien mahdollistamisen ja naamioiden käsitteitä. Lisäksi sinun on tiedettävä, että tällä menettelyllä luotuja tapahtumia ei lähetetä tapahtumajonoon ja ne toimivat vain rekisteröityneiden kuuntelijoiden kanssa.

Tällä hetkellä Java-ydin koostuu 12: ssa määritellystä tapahtumasta java.awt.events:

  • ActionEvent
  • AdjustmentEvent
  • ComponentEvent
  • ContainerEvent
  • FocusEvent
  • InputEvent
  • ItemEvent
  • KeyEvent
  • HiiriTapahtuma
  • PaintEvent
  • TextEvent
  • IkkunaTapahtuma

Koska uusien tapahtumatyyppien luominen ei ole vähäpätöinen tehtävä, sinun tulee tutkia tapahtumia, jotka ovat osa Java-ydintä. Jos mahdollista, yritä käyttää näitä tyyppejä uusien luomisen sijaan.

On kuitenkin aikoja, jolloin uusi tapahtumatyyppi on kehitettävä uudelle komponentille. Tässä keskustelussa käytän esimerkkiä yksinkertaisesta komponentista, ohjatusta paneelista, keinona osoittaa, miten uusi tapahtumalaji luodaan.

Ohjattu paneeli toteuttaa yksinkertaisen velho käyttöliittymä. Komponentti koostuu korttipaneelista, jota voidaan edetä NEXT-painikkeella. TAKAISIN-painikkeella voit siirtyä edelliseen paneeliin. FINISH- ja CANCEL-painikkeet ovat myös käytettävissä.

Jotta komponentti olisi joustava, halusin antaa täydellisen hallinnan kaikkien painikkeiden toiminnoille sitä käyttävälle kehittäjälle. Esimerkiksi, kun NEXT-painiketta painetaan, kehittäjän pitäisi olla mahdollista tarkistaa ensin, onko vaaditut tiedot syötetty tällä hetkellä näkyvään komponenttiin ennen seuraavaan komponenttiin siirtymistä.

Oman tapahtumatyypin luomisessa on viisi päätehtävää:

  • Luo tapahtumakuuntelija

  • Luo kuuntelusovitin

  • Luo tapahtumaluokka

  • Muokkaa komponenttia

  • Useiden kuuntelijoiden hallinta

Tutkimme jokaista näistä tehtävistä vuorotellen ja sitten yhdistämme ne kaikki.

Luo tapahtumakuuntelija

Yksi tapa (ja on monia) kertoa esineille tietyn toiminnan tapahtumisesta on luoda uusi tapahtumatyyppi, joka voidaan toimittaa rekisteröidyille kuuntelijoille. Ohjatun toiminnon paneelin tapauksessa kuuntelijan tulisi tukea neljää erilaista tapahtumaa, yksi kullekin painikkeelle.

Aloitan luomalla kuuntelijan käyttöliittymän. Kullekin painikkeelle määritän kuuntelijan menetelmän seuraavasti:

tuo java.util.EventListener; julkinen käyttöliittymä WizardListener laajentaa EventListener {public abstract void nextSelected (WizardEvent e); public abstract void backSelected (WizardEvent e); julkinen abstrakti void cancelSelected (WizardEvent e); julkinen abstrakti void finishValittu (WizardEvent e); } 

Kullekin menetelmälle on yksi argumentti: Ohjattu tapahtuma, joka määritetään seuraavaksi. Huomaa, että käyttöliittymä ulottuu EventListener, käytetään tunnistamaan tämä käyttöliittymä AWT-kuuntelijana.

Luo kuuntelusovitin

Kuuntelusovittimen luominen on valinnainen vaihe. AWT: ssä kuuntelusovitin on luokka, joka tarjoaa oletustoteutuksen kaikille tietyn kuuntelijatyypin menetelmille. Kaikki adapterin luokat java.awt.event paketti tarjoaa tyhjät menetelmät, jotka eivät tee mitään. Tässä on sovitinluokka WizardListener:

public class WizardAdapter toteuttaa WizardListener {public void nextSelected (WizardEvent e) {} public void backSelected (WizardEvent e) {} public void cancelSelected (WizardEvent e) {} public void finishSelected (WizardEvent e) {}} 

Kun kirjoitat luokan, jonka on oltava ohjattu kuuntelija, on mahdollista laajentaa luokkaa Ohjattu sovitin ja tarjota käyttöön (tai ohittaa) vain kiinnostavat kuuntelumenetelmät. Tämä on ehdottomasti mukavuusluokka.

Luo tapahtumaluokka

Seuraava askel on luoda todellinen Tapahtuma luokka täällä: Ohjattu tapahtuma.

tuo java.awt.AWTEvent; public class WizardEvent laajentaa AWTEvent {public static final int WIZARD_FIRST = AWTEvent.RESERVED_ID_MAX + 1; julkinen staattinen lopullinen int NEXT_SELECTED = WIZARD_FIRST; julkinen staattinen lopullinen int BACK_SELECTED = WIZARD_FIRST + 1; julkinen staattinen lopullinen int CANCEL_SELECTED = WIZARD_FIRST + 2; julkinen staattinen loppu int FINISH_SELECTED = WIZARD_FIRST + 3; julkinen staattinen loppu int WIZARD_LAST = WIZARD_FIRST + 3; public WizardEvent (Wizard source, int id) {super (source, id); }} 

Kaksi vakiota, WIZARD_FIRST ja WIZARD_LAST, merkitse kattava valikoima maskeja, joita tämä tapahtumaluokka käyttää. Huomaa, että tapahtuman tunnukset käyttävät RESERVED_ID_MAX luokan vakio AWTEvent määrittää tunnusten alue, joka ei ole ristiriidassa AWT: n määrittelemien tapahtumien ID-arvojen kanssa. Kun lisää AWT-komponentteja lisätään, RESERVED_ID_MAX voi kasvaa tulevaisuudessa.

Loput neljä vakiota edustavat neljää tapahtuman tunnusta, joista kukin vastaa eri toimintatyyppiä, ohjatun toiminnon määrittelemällä tavalla.

Tapahtuman tunnus ja tapahtuman lähde ovat kaksi argumenttia ohjatun toiminnon tapahtumarakentajalle. Tapahtuman lähteen on oltava tyyppiä Wizard - se on komponenttityyppi, jolle tapahtuma on määritelty. Perustelu on, että vain ohjattu paneeli voi olla ohjatun toiminnon lähde. Huomaa, että Ohjattu tapahtuma luokka jatkuu AWTEvent.

Muokkaa komponenttia

Seuraava vaihe on varustaa komponenttimme menetelmillä, joiden avulla se voi rekisteröidä ja poistaa kuuntelijat uudesta tapahtumasta.

Tapahtuman toimittamiseksi kuuntelijalle kutsutaan tavallisesti asianmukaista tapahtumakuuntelumenetelmää (tapahtumapeitteestä riippuen). Voin rekisteröidä toiminnan kuuntelijan vastaanottamaan toimintatapahtumia NEXT-painikkeesta ja välittämään ne rekisteröidyiksi WizardListener esineitä. actionPerformed NEXT (tai muiden toimintojen) painikkeen toimintakuuntelijan menetelmä voidaan toteuttaa seuraavasti:

public void actionPerformed (ActionEvent e) {// tee mitään, jos kuuntelijoita ei ole rekisteröity, jos (wizardListener == null) palaa; WizardEvent w; Ohjatun toiminnon lähde = tämä; if (e.getSource () == nextButton) {w = uusi WizardEvent (lähde, WizardEvent.NEXT_SELECTED); wizardListener.nextSelected (w); } // käsittele muita ohjatun toiminnon painikkeita samalla tavalla} 

Huomaa: Yllä olevassa esimerkissäWizardpaneeli itse on SEURAAVA -painiketta.

Kun NEXT-painiketta painetaan, uusi Ohjattu tapahtuma luodaan sopivalla lähteellä ja maskilla, joka vastaa painettavaa NEXT-painiketta.

Esimerkissä rivi

 wizardListener.nextSelected (w); 

viittaa wizardListener objekti, joka on yksityinen jäsenmuuttuja Wizard ja on tyypiltään WizardListener. Olemme määrittäneet tämän tyypin ensimmäisenä vaiheena uuden komponenttitapahtuman luomisessa.

Ensi silmäyksellä yllä oleva koodi näyttää rajoittavan kuuntelijoiden määrän yhteen. Yksityinen muuttuja wizardListener ei ole matriisi, ja vain yksi seuraavaValittu puhelu soitetaan. Selitämme, miksi yllä oleva koodi ei todellakaan aseta tätä rajoitusta, tutkitaan, miten kuuntelijat lisätään.

Jokaisen uuden (ennalta määritellyn tai uuden) tapahtuman generoivan komponentin on annettava kaksi tapaa: yksi kuuntelijan lisäyksen ja toinen kuuntelijan poistamisen tukemiseksi. Jos kyseessä on Wizard luokassa nämä menetelmät ovat:

 julkinen synkronoitu void addWizardListener (WizardListener l) {wizardListener = WizardEventMulticaster.add (wizardListener, l); } julkinen synkronoitu void removeWizardListener (WizardListener l) {wizardListener = WizardEventMulticaster.remove (wizardListener, l); } 

Molemmat menetelmät kutsuvat staattisen menetelmän luokan jäseniä WizardEventMulticaster.

Useiden kuuntelijoiden hallinta

Vaikka on mahdollista käyttää a Vektori Jos haluat hallita useita kuulijoita, JDK 1.1 määrittelee erityisluokan kuuntelijaluettelon ylläpitoon: AWTEventMulticaster. Yksi multicaster-ilmentymä ylläpitää viittauksia kahteen kuuntelijaobjektiin. Koska monilähetys on myös itse kuuntelija (se toteuttaa kaikki kuuntelijan käyttöliittymät), kukin kuulemastaan ​​kuuntelijasta voi myös olla monilähetys, mikä luo tapahtumakuuntelijoiden tai monilähetysketjun ketjun:

Jos kuuntelija on myös monilähetys, se edustaa ketjun linkkiä. Muuten se on vain kuuntelija ja siten ketjun viimeinen elementti.

Valitettavasti ei ole mahdollista yksinkertaisesti käyttää uudelleen AWTEventMulticaster käsitellä tapahtumien ryhmälähetystä uusille tapahtumille. Parasta, mitä voidaan tehdä, on laajentaa AWT-multicasteria, vaikka tämä toiminto onkin melko kyseenalainen. AWTEventMulticaster sisältää 56 menetelmää. Näistä 51 menetelmää tukee 12 tapahtumalajia ja niitä vastaavia AWT: n kuulijoita. Jos alaluokka AWTEventMulticaster, et koskaan käytä niitä. Viidestä muusta menetelmästä addInternal (EventListener, EventListener)ja poista (EventListener) täytyy koodata. (Sanon, että on koodattu, koska AWTEventMulticaster, addInternal on staattinen menetelmä eikä sitä siksi voida ylikuormittaa. Siksi tuntemattomista syistä Poista soittaa addInternal ja se on ylikuormitettava.)

Kaksi menetelmää, Tallentaa ja saveInternal, tarjota tukea kohteiden suoratoistoon ja voidaan käyttää uudessa multicaster-luokassa. Viimeinen menetelmä, joka tukee kuuntelijoiden rutiineja, removeInternal, voidaan myös käyttää uudelleen, jos Poista ja addInternal on toteutettu.

Yksinkertaisuuden vuoksi aion alaluokkaan AWTEventMulticaster, mutta koodaaminen on mahdollista vain hyvin pienellä vaivalla Poista, Tallentaaja saveInternal ja niillä on täysin toimiva, itsenäinen tapahtumien multicaster.

Tässä on tapahtuman multicaster sellaisena kuin se on toteutettu käsittelemään Ohjattu tapahtuma:

tuo java.awt.AWTEventMulticaster; tuo java.util.EventListener; public class WizardEventMulticaster laajentaa AWTEventMulticaster toteuttaa WizardListener {suojattu WizardEventMulticaster (EventListener a, EventListener b) {super (a, b); } julkinen staattinen WizardListener add (WizardListener a, WizardListener b) {return (WizardListener) addInternal (a, b); } julkinen staattinen WizardListener poista (WizardListener l, WizardListener oldl) {return (WizardListener) removeInternal (l, oldl); } public void nextSelected (WizardEvent e) {// lähetyspoikkeusta ei koskaan tapahdu tässä tapauksessa // casting _it_ tarvitaan, koska tämä multicaster voi // käsitellä useampaa kuin yhtä kuuntelijaa, jos (a! = null) ((WizardListener) a). seuraavaValittu (e); if (b! = null) ((WizardListener) b) .sextSelected (e); } public void backSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .backSelected (e); if (b! = null) ((WizardListener) b) .backSelected (e); } public void cancelSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .cancelSelected (e); if (b! = null) ((WizardListener) b) .cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .finishSelected (e); if (b! = null) ((WizardListener) b) .finishSelected (e); } suojattu staattinen EventListener addInternal (EventListener a, EventListener b) {if (a == null) return b; jos (b == null) palauttaa a; palauta uusi WizardEventMulticaster (a, b); } suojattu EventListener poista (EventListener oldl) {if (oldl == a) return b; jos (oldl == b) palauttaa a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); jos (a2 == a && b2 == b) palauttaa tämän; return addInternal (a2, b2); }} 

Menetelmät monilähetysluokassa: Katsaus

Tarkastellaan menetelmiä, jotka ovat osa yllä olevaa multicaster-luokkaa. Rakentaja on suojattu ja uuden saamiseksi WizardEventMulticaster, staattinen lisää (WizardListener, WizardListener) menetelmä on kutsuttava. Kytke kaksi kuuntelijaa argumentteina, jotka edustavat kahta kuunteluketjun osaa:

  • Aloita uusi ketju käyttämällä null-argumenttia ensimmäisenä argumenttina.

  • Lisää uusi kuuntelija käyttämällä olemassa olevaa kuuntelijaa ensimmäisenä argumenttina ja uutta kuuntelijaa toisena argumenttina.

Itse asiassa tämä on tehty luokan koodissa Wizard jonka olemme jo tutkineet.

Toinen staattinen rutiini on poista (WizardListener, WizardListener). Ensimmäinen argumentti on kuuntelija (tai kuuntelijan multicaster) ja toinen on poistettava kuuntelija.

Neljä julkista, ei-staattista menetelmää lisättiin tukemaan tapahtumien etenemistä tapahtumaketjussa. Jokaiselle Ohjattu tapahtuma tapauksessa (eli seuraava, takaisin, peruuta ja lopeta valittu) on yksi menetelmä. Nämä menetelmät on pantava täytäntöön, koska WizardEventMulticaster työvälineet WizardListener, mikä puolestaan ​​vaatii neljän menetelmän olevan läsnä.

Kuinka kaikki toimii yhdessä

Tarkastellaan nyt, miten multicasteria todella käytetään Wizard. Oletetaan, että ohjattu objekti on rakennettu ja kolme kuuntelijaa lisätään, mikä luo kuunteluketjun.

Aluksi yksityinen muuttuja wizardListener luokan Wizard on nolla. Joten kun soitetaan WizardEventMulticaster.add (WizardListener, WizardListener), ensimmäinen väite, wizardListener, on nolla ja toinen ei (ei ole järkevää lisätä nolla kuuntelijaa). lisätä menetelmä puolestaan ​​kutsuu addInternal. Koska yksi argumenteista on tyhjä, arvon palautus addInternal on ei-nolla kuuntelija. Paluu etenee lisätä menetelmä, joka palauttaa ei-nolla kuuntelijan addWizardListener menetelmä. Siellä wizardListener muuttuja on asetettu lisäämään uusi kuuntelija.

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