Programování

Tip pro Javu 35: Vytvářejte nové typy událostí v Javě

I když JDK 1.1 určitě zavedením modelu události delegování zjednodušila zpracování událostí, vývojářům to usnadňuje vytváření vlastních typů událostí. Zde popsaný základní postup je ve skutečnosti poměrně přímočarý. Kvůli jednoduchosti nebudu pojednávat o pojmech umožňování událostí a maskách událostí. Navíc byste měli vědět, že události vytvořené pomocí tohoto postupu nebudou zaúčtovány do fronty událostí a budou fungovat pouze s registrovanými posluchači.

V současné době se jádro Java skládá z 12 typů událostí definovaných v události java.awt:

  • ActionEvent
  • Událost úpravy
  • ComponentEvent
  • ContainerEvent
  • FocusEvent
  • InputEvent
  • ItemEvent
  • KeyEvent
  • MouseEvent
  • PaintEvent
  • TextEvent
  • WindowEvent

Protože vytváření nových typů událostí je netriviální úkol, měli byste prozkoumat události, které jsou součástí základní Javy. Pokud je to možné, zkuste tyto typy použít místo vytváření nových.

Nastanou však chvíle, kdy bude nutné pro novou komponentu vyvinout nový typ události. Pro účely této diskuse použiji příklad jednoduché komponenty, panelu průvodce, jako prostředek k předvedení, jak vytvořit nový typ události.

Průvodce panel implementuje jednoduchý kouzelník rozhraní. Součást se skládá z panelu karty, který lze rozšířit pomocí tlačítka DALŠÍ. Tlačítko ZPĚT umožňuje přepnout na předchozí panel. K dispozici jsou také tlačítka FINISH a CANCEL.

Aby byla součást flexibilní, chtěl jsem vývojáři, který ji používá, poskytnout plnou kontrolu nad akcemi provedenými všemi tlačítky. Například když stisknete tlačítko DALŠÍ, mělo by být možné, aby vývojář nejprve zkontroloval, zda byla zadána požadovaná data na aktuálně viditelné komponentě, než přejde na další komponentu.

Při vytváření vlastního typu události existuje pět hlavních úkolů:

  • Vytvořte posluchače událostí

  • Vytvořte adaptér posluchače

  • Vytvořte třídu události

  • Upravte komponentu

  • Správa více posluchačů

Postupně prozkoumáme každý z těchto úkolů a poté je všechny spojíme.

Vytvořte posluchače událostí

Jedním ze způsobů (a je jich mnoho) informování objektů, že došlo k určité akci, je vytvoření nového typu události, který by mohl být doručen registrovaným posluchačům. V případě panelu průvodce by měl posluchač podporovat čtyři různé případy událostí, jeden pro každé tlačítko.

Začnu vytvořením rozhraní posluchače. Pro každé tlačítko definuji metodu posluchače následujícím způsobem:

import java.util.EventListener; veřejné rozhraní WizardListener rozšiřuje EventListener {public abstract void nextSelected (WizardEvent e); public abstract void backSelected (WizardEvent e); public abstract void cancelSelected (WizardEvent e); public abstract void finishSelected (WizardEvent e); } 

Každá metoda má jeden argument: WizardEvent, který je definován dále. Rozhraní se rozšiřuje EventListener, slouží k identifikaci tohoto rozhraní jako posluchače AWT.

Vytvořte adaptér posluchače

Vytvoření adaptéru pro posluchače je volitelný krok. V AWT je adaptér posluchače třída, která poskytuje výchozí implementaci pro všechny metody určitého typu posluchače. Všechny třídy adaptérů v událost java.awt balíček poskytuje prázdné metody, které nic nedělají. Zde je třída adaptéru pro WizardListener:

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

Při psaní třídy, která má být posluchačem průvodce, je možné rozšířit WizardAdapter a zajistit implementaci (nebo přepsat) pouze ty metody posluchače, které jsou zajímavé. Jedná se přísně o třídu pohodlí.

Vytvořte třídu události

Dalším krokem je vytvoření skutečného událost třída zde: WizardEvent.

import java.awt.AWTEvent; public class WizardEvent extends AWTEvent {public static final int WIZARD_FIRST = AWTEvent.RESERVED_ID_MAX + 1; public static final int NEXT_SELECTED = WIZARD_FIRST; public static final int BACK_SELECTED = WIZARD_FIRST + 1; public static final int CANCEL_SELECTED = WIZARD_FIRST + 2; public static final int FINISH_SELECTED = WIZARD_FIRST + 3; public static final int WIZARD_LAST = WIZARD_FIRST + 3; public WizardEvent (zdroj průvodce, int id) {super (zdroj, id); }} 

Dvě konstanty, WIZARD_FIRST a WIZARD_LAST, označte inkluzivní rozsah masek používaných touto třídou událostí. Všimněte si, že ID událostí používají RESERVED_ID_MAX konstanta třídy AWTEvent k určení rozsahu ID, která nebudou v rozporu s hodnotami ID události definovanými AWT. Jak jsou přidávány další komponenty AWT, RESERVED_ID_MAX se v budoucnu může zvýšit.

Zbývající čtyři konstanty představují čtyři ID událostí, z nichž každé odpovídá jinému typu akce, jak je definováno funkcí průvodce.

ID události a zdroj události jsou dva argumenty pro konstruktor události průvodce. Zdroj události musí být typu kouzelník - to je typ komponenty, pro kterou je událost definována. Důvodem je, že zdrojem událostí průvodce může být pouze panel průvodce. Všimněte si, že WizardEvent třída se prodlužuje AWTEvent.

Upravte komponentu

Dalším krokem je vybavení naší komponenty metodami, které jí umožňují registrovat a odebírat posluchače nové události.

Chcete-li doručit událost posluchači, obvykle byste zavolali příslušnou metodu posluchače událostí (v závislosti na masce události). Mohu zaregistrovat posluchače akcí, který bude přijímat akce z tlačítka NEXT a předávat je registrovaným WizardListener předměty. The akce Provedeno metodu posluchače akcí pro tlačítko DALŠÍ (nebo jiné akce) lze implementovat takto:

public void actionPerformed (ActionEvent e) {// nedělat nic, pokud nejsou zaregistrováni žádní posluchači, pokud se (wizardListener == null) vrátí; WizardEvent w; Zdroj průvodce = this; if (e.getSource () == nextButton) {w = nový WizardEvent (zdroj, WizardEvent.NEXT_SELECTED); wizardListener.nextSelected (w); } // zpracovat zbytek tlačítek průvodce podobným způsobem} 

Poznámka: Ve výše uvedeném příkladu jekouzelníksamotný panel je posluchačem souboru DALŠÍ knoflík.

Po stisknutí tlačítka NEXT se objeví nové WizardEvent je vytvořen s příslušným zdrojem a maskou, která odpovídá stisknutému tlačítku NEXT.

V příkladu řádek

 wizardListener.nextSelected (w); 

Odkazuje na průvodce čarodějů objekt, který je soukromou členskou proměnnou pro kouzelník a je typu WizardListener. Tento typ jsme definovali jako první krok při vytváření nové události komponenty.

Na první pohled se zdá, že výše uvedený kód omezuje počet posluchačů na jednoho. Soukromá proměnná průvodce čarodějů není pole a pouze jedno dalšíVybráno je proveden hovor. Abychom vysvětlili, proč výše uvedený kód ve skutečnosti nepředstavuje toto omezení, prozkoumejme, jak jsou přidáni posluchači.

Každá nová součást, která generuje události (předdefinované nebo nové), musí poskytovat dvě metody: jednu pro podporu přidání posluchače a jednu pro podporu odstranění posluchače. V případě kouzelník třídy, tyto metody jsou:

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

Obě metody volají členy statické metody třídy WizardEventMulticaster.

Správa více posluchačů

I když je možné použít a Vektor pro správu více posluchačů definuje JDK 1.1 speciální třídu pro udržování seznamu posluchačů: AWTEventMulticaster. Jedna instance vícesměrového vysílání udržuje odkazy na dva objekty posluchače. Protože multicaster je také samotným posluchačem (implementuje všechna rozhraní posluchače), každý ze dvou posluchačů, které sleduje, může být také multicaster, což vytváří řetězec posluchačů událostí nebo multicasterů:

Pokud je posluchač také multicaster, pak představuje odkaz v řetězci. Jinak je pouze posluchačem a je tedy posledním prvkem v řetězci.

Bohužel není možné jednoduše znovu použít AWTEventMulticaster zpracovat vícesměrové vysílání událostí pro nové typy událostí. Nejlepší, co lze udělat, je rozšířit AWT multicaster, i když tato operace je docela sporná. AWTEventMulticaster obsahuje 56 metod. Z toho 51 metod poskytuje podporu pro 12 typů událostí a jejich odpovídající posluchače, které jsou součástí AWT. Pokud podtřídu AWTEventMulticaster, stejně je nikdy nepoužijete. Ze zbývajících pěti metod addInternal (EventListener, EventListener), a remove (EventListener) je třeba překódovat. (Říkám překódováno, protože v AWTEventMulticaster, addInternal je statická metoda, a proto ji nelze přetížit. Z dosud neznámých důvodů odstranit volá addInternal a je třeba jej přetížit.)

Dvě metody, Uložit a saveInternal, poskytují podporu pro streamování objektů a lze je znovu použít v nové třídě multicaster. Poslední metoda, která podporuje rutiny odebrání posluchače, removeInternal, lze také znovu použít za předpokladu, že nové verze odstranit a addInternal byly implementovány.

Kvůli jednoduchosti se chystám podtřídu AWTEventMulticaster, ale s velmi malým úsilím je možné kódovat odstranit, Uložit, a saveInternal a mít plně funkční, samostatný multicaster událostí.

Zde je multicaster událostí implementovaný ke zpracování WizardEvent:

importovat java.awt.AWTEventMulticaster; import java.util.EventListener; veřejná třída WizardEventMulticaster rozšiřuje AWTEventMulticaster implementuje WizardListener {chráněný WizardEventMulticaster (EventListener a, EventListener b) {super (a, b); } public static WizardListener add (WizardListener a, WizardListener b) {return (WizardListener) addInternal (a, b); } public static WizardListener remove (WizardListener l, WizardListener oldl) {return (WizardListener) removeInternal (l, oldl); } public void nextSelected (WizardEvent e) {// casting exception will never occurs in this case // casting _is_ needed because this multicaster may // handle more than just one listener if (a! = null) ((WizardListener) a). nextSelected (e); if (b! = null) ((WizardListener) b) .nextSelected (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); } chráněný statický EventListener addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) vrátit a; vrátit nový WizardEventMulticaster (a, b); } chráněný EventListener remove (EventListener oldl) {if (oldl == a) return b; if (oldl == b) vrátit a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); if (a2 == a && b2 == b) vrátit toto; návrat addInternal (a2, b2); }} 

Metody ve třídě multicaster: Recenze

Podívejme se na metody, které jsou součástí třídy multicaster výše. Konstruktor je chráněn a aby získal nový WizardEventMulticaster, statický přidat (WizardListener, WizardListener) metoda musí být volána. Trvá dva posluchače jako argumenty, které představují dva kusy řetězce posluchače, které mají být propojeny:

  • Chcete-li spustit nový řetězec, použijte null jako první argument.

  • Chcete-li přidat nového posluchače, použijte existující posluchač jako první argument a nový posluchač jako druhý argument.

To je ve skutečnosti to, co bylo provedeno v kódu třídy kouzelník které jsme již prozkoumali.

Další statická rutina je remove (WizardListener, WizardListener). První argument je posluchač (nebo multicaster posluchače) a druhý je posluchač, který má být odstraněn.

Byly přidány čtyři veřejné, nestatické metody pro podporu šíření událostí prostřednictvím řetězce událostí. Pro každého WizardEvent případ (tj. vybraný další, zpět, zrušit a dokončit) existuje jedna metoda. Tyto metody musí být implementovány, protože WizardEventMulticaster nářadí WizardListener, což vyžaduje přítomnost čtyř metod.

Jak to všechno funguje společně

Pojďme nyní prozkoumat, jak multicaster ve skutečnosti používá kouzelník. Předpokládejme, že je vytvořen objekt průvodce a jsou přidáni tři posluchači, čímž se vytvoří řetězec posluchačů.

Zpočátku soukromá proměnná průvodce čarodějů třídy kouzelník je null. Když se tedy volá WizardEventMulticaster.add (WizardListener, WizardListener)první argument, průvodce čarodějů, je null a druhý není (nemá smysl přidávat nulový posluchač). The přidat metoda zase volá addInternal. Protože jeden z argumentů je null, návrat z addInternal je nenulový posluchač. Návrat se šíří do přidat metoda, která vrací posluchače, který nemá hodnotu null addWizardListener metoda. Tady průvodce čarodějů proměnná je nastavena na přidávaného nového posluchače.