Java Tip 35: Buat jenis acara baru di Java

Meskipun JDK 1.1 jelas memiliki penanganan acara yang efisien dengan pengenalan model acara delegasi, hal ini tidak memudahkan pengembang untuk membuat jenis acara mereka sendiri. Prosedur dasar yang dijelaskan di sini sebenarnya cukup mudah. Demi kesederhanaan, saya tidak akan membahas konsep pengaktifan acara dan topeng acara. Selain itu, Anda harus tahu bahwa acara yang dibuat menggunakan prosedur ini tidak akan dikirim ke antrian acara dan hanya akan bekerja dengan pendengar terdaftar.

Saat ini, inti Java terdiri dari 12 jenis peristiwa yang ditentukan dalam java.awt.events:

  • ActionEvent
  • AdjustmentEvent
  • ComponentEvent
  • ContainerEvent
  • FocusEvent
  • InputEvent
  • ItemEvent
  • KeyEvent
  • MouseEvent
  • PaintEvent
  • TextEvent
  • WindowEvent

Karena membuat jenis peristiwa baru adalah tugas yang tidak sepele, Anda harus memeriksa peristiwa yang merupakan bagian dari Java inti. Jika memungkinkan, coba gunakan jenis tersebut daripada membuat yang baru.

Akan ada waktu, bagaimanapun, ketika jenis acara baru perlu dikembangkan untuk komponen baru. Untuk keperluan diskusi ini, saya akan menggunakan contoh komponen sederhana, panel wizard, sebagai sarana untuk mendemonstrasikan cara membuat jenis acara baru.

Panel wizard mengimplementasikan antarmuka wizard sederhana . Komponen tersebut terdiri dari panel kartu yang dapat dimajukan menggunakan tombol NEXT. Tombol BACK memungkinkan Anda untuk beralih ke panel sebelumnya. Tombol FINISH dan CANCEL juga disediakan.

Untuk membuat komponen fleksibel, saya ingin memberikan kontrol penuh atas tindakan yang diambil oleh semua tombol kepada pengembang yang menggunakannya. Misalnya, ketika tombol NEXT ditekan, pengembang dapat terlebih dahulu memeriksa apakah data yang diperlukan telah dimasukkan pada komponen yang saat ini terlihat sebelum melanjutkan ke komponen berikutnya.

Ada lima tugas utama dalam membuat jenis acara Anda sendiri:

  • Buat pendengar acara

  • Buat adaptor pendengar

  • Buat kelas acara

  • Ubah komponen

  • Mengelola banyak pendengar

Kami akan memeriksa setiap tugas ini secara bergiliran dan kemudian menggabungkan semuanya.

Buat pendengar acara

Salah satu cara (dan ada banyak) untuk memberi tahu objek bahwa tindakan tertentu telah terjadi adalah dengan membuat jenis peristiwa baru yang dapat dikirimkan ke pendengar terdaftar. Untuk panel wizard, pendengar harus mendukung empat kasus acara yang berbeda, satu untuk setiap tombol.

Saya mulai dengan membuat antarmuka pendengar. Untuk setiap tombol, saya mendefinisikan metode pendengar dengan cara berikut:

import java.util.EventListener; antarmuka publik WizardListener extends 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); }

Setiap metode mengambil satu argumen:, WizardEventyang ditentukan selanjutnya. Perhatikan bahwa antarmuka meluas EventListener, digunakan untuk mengidentifikasi antarmuka ini sebagai pendengar AWT.

Buat adaptor pendengar

Membuat adaptor pendengar adalah langkah opsional. Di AWT, adaptor pendengar adalah kelas yang menyediakan implementasi default untuk semua metode dari jenis pendengar tertentu. Semua kelas adaptor dalam java.awt.eventpaket menyediakan metode kosong yang tidak melakukan apa-apa. Berikut adalah kelas adaptor untuk WizardListener:

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

Saat menulis kelas yang akan menjadi pendengar wizard, dimungkinkan untuk memperluas WizardAdapterdan menyediakan implementasi untuk (atau mengganti) hanya metode pendengar yang diinginkan. Ini benar-benar kelas kenyamanan.

Buat kelas acara

Langkah berikutnya adalah untuk menciptakan aktual Eventkelas di sini: WizardEvent.

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

Dua konstanta, WIZARD_FIRSTdan WIZARD_LAST, tandai kisaran inklusif topeng yang digunakan oleh kelas Peristiwa ini. Perhatikan bahwa ID peristiwa menggunakan RESERVED_ID_MAXkonstanta kelas AWTEventuntuk menentukan kisaran ID yang tidak akan konflik dengan nilai ID peristiwa yang ditentukan oleh AWT. Karena lebih banyak komponen AWT ditambahkan, RESERVED_ID_MAXkemungkinan akan meningkat di masa mendatang.

Empat konstanta lainnya mewakili empat ID peristiwa, masing-masing sesuai dengan jenis tindakan yang berbeda, seperti yang ditentukan oleh fungsionalitas wizard.

ID peristiwa dan sumber peristiwa adalah dua argumen untuk konstruktor peristiwa wizard. Sumber acara harus berjenis Wizard- yaitu jenis komponen tempat acara ditentukan. Alasannya adalah bahwa hanya panel wizard yang dapat menjadi sumber acara wizard. Perhatikan bahwa WizardEventkelas diperluas AWTEvent.

Ubah komponen

Langkah selanjutnya adalah melengkapi komponen kita dengan metode yang memungkinkannya untuk mendaftar dan menghapus listener untuk acara baru.

Untuk mengirimkan acara ke pemroses, biasanya seseorang akan memanggil metode pemroses acara yang sesuai (bergantung pada topeng acara). Saya dapat mendaftarkan pendengar tindakan untuk menerima peristiwa tindakan dari tombol NEXT dan menyampaikannya ke WizardListenerobjek terdaftar . The actionPerformedmetode pendengar tindakan untuk BERIKUTNYA (atau tindakan lainnya) tombol dapat diterapkan sebagai berikut:

public void actionPerformed (ActionEvent e) {// tidak melakukan apa pun jika tidak ada listener yang terdaftar if (wizardListener == null) return; WizardEvent w; Sumber penyihir = ini; if (e.getSource () == nextButton) {w = new WizardEvent (sumber, WizardEvent.NEXT_SELECTED); wizardListener.nextSelected (w); } // tangani tombol wizard lainnya dengan cara yang sama}

Catatan: Pada contoh di atas, Wizardpanel itu sendiri adalah pendengar untuk tombol NEXT .

Saat tombol NEXT ditekan, yang baru WizardEventdibuat dengan sumber dan topeng yang sesuai yang sesuai dengan tombol NEXT yang ditekan.

Dalam contoh, garis

 wizardListener.nextSelected (w); 

mengacu pada wizardListenerobjek yang merupakan variabel anggota privat untuk Wizarddan berjenis WizardListener. Kami telah mendefinisikan tipe ini sebagai langkah pertama dalam membuat acara komponen baru.

Sekilas, kode di atas sepertinya membatasi jumlah pendengar menjadi satu. Variabel privat wizardListenerbukanlah sebuah array, dan hanya satu nextSelectedpanggilan yang dibuat. Untuk menjelaskan mengapa kode di atas sebenarnya tidak menimbulkan batasan itu, mari kita periksa bagaimana pendengar ditambahkan.

Setiap komponen baru yang menghasilkan peristiwa (sudah ditentukan sebelumnya atau baru) perlu menyediakan dua metode: satu untuk mendukung penambahan listener dan satu lagi untuk mendukung penghapusan listener. Dalam kasus Wizardkelas, metode ini adalah:

publik disinkronkan void addWizardListener (WizardListener l) {wizardListener = WizardEventMulticaster.add (wizardListener, l); } hapusWizardListener (WizardListener l) yang disinkronkan publik {wizardListener = WizardEventMulticaster.remove (wizardListener, l); }

Kedua metode melakukan panggilan ke metode statis anggota kelas WizardEventMulticaster.

Mengelola banyak pendengar

Meskipun dimungkinkan untuk menggunakan Vectoruntuk mengelola beberapa pendengar, JDK 1.1 mendefinisikan kelas khusus untuk menjaga daftar pendengar: AWTEventMulticaster. Sebuah instance multicaster tunggal memelihara referensi ke dua objek pendengar. Karena multicaster juga merupakan pendengar itu sendiri (mengimplementasikan semua antarmuka pendengar), masing-masing dari dua pendengar yang dilacaknya juga dapat menjadi multicaster, sehingga menciptakan rangkaian pendengar acara atau multicaster:

Jika pendengar juga merupakan multicaster, maka itu mewakili tautan dalam rantai. Jika tidak, itu hanyalah pendengar dan dengan demikian merupakan elemen terakhir dalam rantai.

Sayangnya, tidak mungkin hanya menggunakan kembali AWTEventMulticasteruntuk menangani acara multicasting untuk jenis acara baru. Hal terbaik yang dapat dilakukan adalah memperluas multicaster AWT, meskipun operasi ini agak meragukan. AWTEventMulticasterberisi 56 metode. Dari jumlah tersebut, 51 metode memberikan dukungan untuk 12 jenis peristiwa dan pemrosesnya yang sesuai yang merupakan bagian dari AWT. Jika Anda membuat subkelas AWTEventMulticaster, Anda tidak akan pernah menggunakannya. Dari lima metode yang tersisa addInternal(EventListener, EventListener),, dan remove(EventListener)perlu dikodekan ulang. (Saya katakan dikodekan ulang karena dalam AWTEventMulticaster, addInternaladalah metode statis dan oleh karena itu tidak dapat kelebihan beban. Untuk alasan yang tidak saya ketahui saat ini, removelakukan panggilan ke addInternaldan perlu kelebihan beban.)

Dua metode, savedan saveInternal, memberikan dukungan untuk streaming objek dan dapat digunakan kembali di kelas multicaster yang baru. Metode terakhir yang mendukung rutinitas penghapusan pendengar removeInternal,, juga dapat digunakan kembali, asalkan versi baru removedan addInternaltelah diterapkan.

Demi kesederhanaan, saya akan subclass AWTEventMulticaster, tapi dengan sedikit usaha, adalah mungkin untuk kode remove, save, dan saveInternaldan memiliki berfungsi penuh, mandiri acara multicaster.

Berikut adalah event multicaster yang diimplementasikan untuk ditangani WizardEvent:

impor java.awt.AWTEventMulticaster; import java.util.EventListener; public class WizardEventMulticaster memperluas AWTEventMulticaster mengimplementasikan WizardListener {protected 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) {// pengecualian casting tidak akan pernah terjadi dalam kasus ini // casting _is_ diperlukan karena multicaster ini // dapat // menangani lebih dari satu 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); } dilindungi statis EventListener addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) mengembalikan a; mengembalikan WizardEventMulticaster baru (a, b); } EventListener dilindungi hapus (EventListener oldl) {if (oldl == a) return b; jika (oldl == b) mengembalikan a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); if (a2 == a && b2 == b) kembalikan ini; return addInternal (a2, b2); }}= 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); } dilindungi statis EventListener addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) mengembalikan a; mengembalikan WizardEventMulticaster baru (a, b); } EventListener dilindungi hapus (EventListener oldl) {if (oldl == a) return b; jika (oldl == b) mengembalikan a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); if (a2 == a && b2 == b) kembalikan ini; return addInternal (a2, b2); }}= 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); } dilindungi statis EventListener addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) mengembalikan a; mengembalikan WizardEventMulticaster baru (a, b); } EventListener dilindungi hapus (EventListener oldl) {if (oldl == a) return b; jika (oldl == b) mengembalikan a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); if (a2 == a && b2 == b) kembalikan ini; return addInternal (a2, b2); }}} 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); } dilindungi statis EventListener addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) mengembalikan a; mengembalikan WizardEventMulticaster baru (a, b); } EventListener dilindungi hapus (EventListener oldl) {if (oldl == a) return b; jika (oldl == b) mengembalikan a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); if (a2 == a && b2 == b) kembalikan ini; return addInternal (a2, b2); }}} 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); } dilindungi statis EventListener addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) mengembalikan a; mengembalikan WizardEventMulticaster baru (a, b); } EventListener dilindungi hapus (EventListener oldl) {if (oldl == a) return b; jika (oldl == b) mengembalikan a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); if (a2 == a && b2 == b) kembalikan ini; return addInternal (a2, b2); }}cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .finishSelected (e); if (b! = null) ((WizardListener) b) .finishSelected (e); } dilindungi statis EventListener addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) mengembalikan a; mengembalikan WizardEventMulticaster baru (a, b); } EventListener dilindungi hapus (EventListener oldl) {if (oldl == a) return b; jika (oldl == b) mengembalikan a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); if (a2 == a && b2 == b) kembalikan ini; return addInternal (a2, b2); }}cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .finishSelected (e); if (b! = null) ((WizardListener) b) .finishSelected (e); } dilindungi statis EventListener addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) mengembalikan a; mengembalikan WizardEventMulticaster baru (a, b); } EventListener dilindungi hapus (EventListener oldl) {if (oldl == a) return b; jika (oldl == b) mengembalikan a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); if (a2 == a && b2 == b) kembalikan ini; return addInternal (a2, b2); }}EventListener b) {if (a == null) return b; if (b == null) mengembalikan a; mengembalikan WizardEventMulticaster baru (a, b); } EventListener dilindungi hapus (EventListener oldl) {if (oldl == a) return b; jika (oldl == b) mengembalikan a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); if (a2 == a && b2 == b) kembalikan ini; return addInternal (a2, b2); }}EventListener b) {if (a == null) return b; if (b == null) mengembalikan a; mengembalikan WizardEventMulticaster baru (a, b); } EventListener dilindungi hapus (EventListener oldl) {if (oldl == a) return b; jika (oldl == b) mengembalikan a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); if (a2 == a && b2 == b) kembalikan ini; return addInternal (a2, b2); }}

Metode di kelas multicaster: Review

Mari kita tinjau metode yang merupakan bagian dari kelas multicaster di atas. Konstruktor dilindungi, dan untuk mendapatkan yang baru WizardEventMulticaster, add(WizardListener, WizardListener)metode statis harus dipanggil. Dibutuhkan dua pendengar sebagai argumen yang mewakili dua bagian rantai pendengar untuk ditautkan:

  • Untuk memulai rantai baru, gunakan null sebagai argumen pertama.

  • Untuk menambahkan listener baru, gunakan listener yang ada sebagai argumen pertama dan listener baru sebagai argumen kedua.

Faktanya, inilah yang telah dilakukan dalam kode untuk kelas Wizardyang telah kita periksa.

Rutinitas statis lainnya adalah remove(WizardListener, WizardListener). Argumen pertama adalah listener (atau multicaster listener), dan yang kedua adalah listener yang akan dihapus.

Four public, non-static methods were added to support event propagation through the event chain. For each WizardEvent case (that is, next, back, cancel, and finish selected) there is one method. These methods must be implemented since the WizardEventMulticaster implements WizardListener, which in turn requires the four methods to be present.

How it all works together

Let's now examine how the multicaster actually is used by the Wizard. Let's suppose a wizard object is constructed and three listeners are added, creating a listener chain.

Awalnya, variabel privat wizardListenerkelas Wizardadalah null. Jadi, ketika panggilan dilakukan ke WizardEventMulticaster.add(WizardListener, WizardListener), argumen pertama wizardListener,, adalah null dan yang kedua tidak (tidak masuk akal untuk menambahkan listener null). The addmetode, pada gilirannya, panggilan addInternal. Karena salah satu argumen bernilai null, hasil dari addInternallistener bukan null. Return menjalar ke addmetode yang mengembalikan listener bukan null ke addWizardListenermetode. Di sana, wizardListenervariabel disetel ke pendengar baru yang ditambahkan.