Pindah ke JDK 1.1: Menggunakan model peristiwa delegasi untuk membuat komponen AWT kustom

Meskipun banyak aplikasi saat ini membutuhkan JDK 1.0.2, transisi ke 1.1 tidak dapat dihindari untuk upaya pengembangan yang serius. Langkah ini memperkenalkan perubahan signifikan pada Abstract Windowing Toolkit (AWT), beberapa di antaranya akan kami bahas di artikel ini. Selama beberapa halaman virtual berikutnya, kita akan membahas cara membuat komponen AWT yang dapat digunakan kembali yang beroperasi dalam model delegasi acara baru. Konsep yang kami bahas juga akan membantu saat Anda perlu memperbarui komponen khusus yang ada untuk bekerja dalam model peristiwa baru. Mereka yang memiliki pandangan ke masa depan harus berusaha untuk menyadari perubahan tambahan yang akan dibawa JDK 1.2, tapi itu cerita lain ....

Untuk mempermudah, kita akan melihat contoh yang cukup mendasar. Tujuan saya adalah menunjukkan kepada Anda cara memperoleh, memproses, dan mengirimkan acara, tanpa terhalang oleh detail pemilihan warna yang rumit.

Antarmuka ColorPicker

Seperti yang Anda lihat pada gambar di bawah ini, komponen ColorPicker terdiri dari tiga wilayah: Wilayah sebelah kiri menampilkan contoh warna, dengan tingkat merah yang bervariasi dari kiri ke kanan di seluruh contoh dan tingkat hijau bervariasi dari atas ke bawah. Pengguna memilih level merah dan hijau dengan mengklik contoh ini. Wilayah tengah menampilkan garis biru vertikal. Pengguna menentukan jumlah biru dengan mengklik lokasi yang tepat di bilah. Wilayah sebelah kanan menampilkan warna saat ini, yang merupakan kombinasi dari tingkat merah, hijau, dan biru yang dipilih pengguna. Mengklik di wilayah ini memilih warna saat ini, menyebabkan terjadinya peristiwa AWT yang sesuai.

Antarmuka ColorPicker

Esensi AWT baru

Daftar berikut mencakup elemen penting dari AWT yang dipengaruhi oleh JDK 1.1, yang berlaku untuk komponen kustom:

  • Model peristiwa - Peristiwa tidak menyebar ke hierarki penampung seperti sebelumnya; sebaliknya, pendengar yang tertarik mendaftarkan diri mereka sendiri dengan komponen AWT, dan acara bersifat multicast melalui listenerantarmuka.

  • Jenis acara - EventKelas monolothic tunggal tidak lagi digunakan untuk pengiriman semua acara; sebagai gantinya, kelas kejadian yang berbeda diturunkan dari java.util.EventObject(atau, sehubungan dengan AWT, java.awt.AWTEvent) dan menyediakan antarmuka yang sesuai untuk kejadian yang relevan.

  • Metode acara - Acara tidak lagi dikirim ke komponen melalui handleEvent()metode tradisional ; sebagai gantinya, processEvent()metode digunakan bersama dengan berbagai pembantu terkait.

  • Masker acara - Masker acara dari acara yang harus dibuat oleh komponen tertentu sekarang dipertahankan. Pendekatan baru ini lebih efisien karena peristiwa tertentu tidak akan dibuat dan diproses jika tidak ada target yang mendengarkannya. Akibatnya, jika Anda membuat subkelas komponen, maka secara default, tidak akan menghasilkan kejadian AWT biasa. Jika Anda ingin menerima peristiwa tertentu, Anda harus secara eksplisit menandai jenis peristiwa yang ingin Anda buat.

  • Nama metode - Banyak metode telah diubah namanya untuk mencapai antarmuka yang lebih konsisten dan mirip kacang. Perubahan ini diperlukan untuk transisi ke model acara baru.

Implementasi ColorPicker

Berikut empat kelas yang mengimplementasikan komponen kita:

  • ColorEvent adalah kelas peristiwa khusus yang mengangkut hasil ColorPicker Color.

  • ColorListener adalah antarmuka yang digunakan oleh pihak yang berkepentingan untuk mendengarkan ColorEvent.

  • ColorPicker adalah komponen pemilih warna grafis yang sebenarnya.

  • ColorEventMulticaster digunakan oleh ColorPickerkelas untuk memelihara daftar terdaftar ColorListener.

Mari kita lihat secara mendetail pada masing-masing kelas ini, dan kemudian saya akan menunjukkan cara kerja komponen ColorPicker kepada Anda.

Kelas ColorEvent

Jenis acara ColorEventdiposting oleh ColorPickerkomponen saat pengguna memilih warna dengan mengklik di wilayah GUI sebelah kanan. Acara tersebut berisi Colorbidang, yang merupakan warna yang dipilih oleh pengguna dan dapat diekstrak melalui getColor()metode.

Setiap acara yang akan digunakan oleh komponen AWT harus merupakan subkelas dari AWTEventkelas tersebut. Dalam kasus ini, kami mendeklarasikan ColorEventsubclass untuk mengangkut peristiwa warna:

impor java.awt.AWTEvent; import java.awt.Color; kelas publik ColorEvent memperluas AWTEvent {

Semua peristiwa AWT harus diberi pengidentifikasi integer; pengenal pengguna yang valid adalah nilai apa pun di atas AWTEvent.RESERVED_ID_MAX.

public int static final COLOR_PICKED = AWTEvent.RESERVED_ID_MAX + 1; 

Karena kelas acara sekarang digunakan sebagai fitur pembeda dan bukan hanya ID-nya, komponen pengguna tidak lagi harus memilih nilai unik global. Sebaliknya, pengenal ini dapat digunakan untuk membedakan di antara berbagai jenis kelas acara tertentu. Misalnya, kita juga bisa mendefinisikan COLOR_CHANGEDpengenal yang mengidentifikasi ketika pengguna telah mengubah pilihan tetapi belum menerima hasilnya. Kemudian kita dapat membedakan antara dua peristiwa dengan satu ColorEventkelas dan dua pengenal, bukan dua kelas peristiwa yang terpisah.

Warna yang terkait dengan acara ini disimpan dalam colorvariabel:

warna Warna yang dilindungi; 

Konstruktor untuk acara ini menerima sumber acara source, yang untuk contoh ini akan menjadi ColorPicker, dan Colorcoloryang dipilih:

public ColorEvent (Sumber objek, Warna warna) {super (sumber, COLOR_PICKED); this.color = color; }

The getColorMetode memungkinkan penerima acara untuk mengekstrak warna yang berhubungan:

public Color getColor () {return color; }

The paramStringMetode hanya merupakan metode kenyamanan yang digunakan ketika AWTEventdicetak ke konsol:

public String paramString () {return "COLOR_PICKED, color =" + color; }

Antarmuka ColorListener

Kelas yang tertarik untuk menerima ColorEventharus mengimplementasikan ColorListenerantarmuka yang mendeklarasikan colorPicked()metode yang akan digunakan untuk menyampaikan peristiwa semacam itu.

Semua antarmuka pemroses acara harus memperluas EventListenerantarmuka dummy:

import java.util.EventListener; antarmuka publik ColorListener memperluas EventListener {

The colorPicked()Metode akan meminta semua pihak tertarik ketika warna telah dipetik. Parameter eberisi yang relevan ColorEvent:

 public void colorPicked (ColorEvent e); 

Kelas ColorPicker

The ColorPickerkelas adalah warna-memilih komponen sederhana yang menggunakan model delegasi acara baru JDK 1.1. Anda menambahkannya ke penampung seperti yang Anda lakukan pada komponen AWT normal, dan ini memberikan peristiwa paradigma baru saat pengguna memilih warna.

Saya sengaja membuat antarmuka pengguna pemilihan warna sangat primitif karena kami prihatin dengan internal 1.1 eventing, bukan kegunaan. Baris kode yang memiliki relevansi khusus dengan JDK 1.1 disorot dengan warna merah. Saya sarankan Anda bereksperimen dengan membuat antarmuka yang lebih ramah.

Kami memperluas Canvaskarena ColorPicker adalah komponen yang sepenuhnya dibuat khusus. Jika kami ingin membuat pemilih warna dari komponen lain, kami akan memperluas Panel:

import java.awt. *; impor java.awt.event.MouseEvent; kelas publik ColorPicker memperluas Canvas {

Pemilih warna mengkuantisasi ruang RGB 0-255 ^ 3 menjadi enam tingkat dari setiap komponen: 0, 51, 102, 153, 204, 255. Ini sesuai dengan kubus warna browser 256 warna yang umum. Untuk perincian pilihan warna yang lebih baik, gunakan lebih banyak level:

dilindungi TINGKAT int akhir statis = 6; 

Level saat ini merah, hijau, dan biru disimpan dalam variabel r, gdan bmasing-masing:

dilindungi int r, g, b; 

In the constructor we extract the various color levels from the initial color color, and then enable mouse events using the enableEvents() method and a mask of AWTEvent.MOUSE_EVENT_MASK. If we did not enable events in this manner, the mouse events would not be generated by this component. We'll get into this in greater detail later when we discuss the processMouseEvent() method.

public ColorPicker (Color color) { r = color.getRed (); g = color.getGreen (); b = color.getBlue (); enableEvents (AWTEvent.MOUSE_EVENT_MASK); } 

The following table shows the event masks in the AWTEvent class that correspond to the different AWT event listeners. Multiple event types can be enabled by either ORing together the masks or by calling enableEvents() repeatedly. Registering a listener for an event type automatically enables the relevant event type.

Event mask Listener interface
ACTION_EVENT_MASK ActionListener
ADJUSTMENT_EVENT_MASK AdjustmentListener
COMPONENT_EVENT_MASK ComponentListener
CONTAINER_EVENT_MASK ContainerListener
FOCUS_EVENT_MASK FocusListener
ITEM_EVENT_MASK ItemListener
KEY_EVENT_MASK KeyListener
MOUSE_EVENT_MASK MouseListener
MOUSE_MOTION_EVENT_MASK MouseMotionListener
TEXT_EVENT_MASK TextListener
WINDOW_EVENT_MASK WindowListener
Event masks and their corresponding listeners

An alternative means for this component to receive its own mouse events would be to implement the MouseListener interface and register as a listener.

This constructor calls the other constructor with an initial color value of black:

public ColorPicker () { this (Color.black); } 

The getPreferredSize() method, shown next, chooses an appropriate size for the component. Under JDK 1.0.2 this method was called preferredSize(). For sake of completeness, we should also implement the getMinimumSize() and getMaximumSize() methods; however, for clarity (not to mention brevity) I've omitted these from this example:

public Dimension getPreferredSize () { return new Dimension (150, 60); } 

Moving right along, the paint() method draws a color swatch on the left, a blue bar with a small blue level marker in the middle, and the current color on the right. The details are not particularly interesting; We choose a block size based on the number of levels desired and the component size and then fill in the blanks:

public void paint (Graphics g) { int h = getSize ().width / (LEVELS + 3 + LEVELS); int v = getSize ().height / (LEVELS); for (int red = 0; red < LEVELS; ++ red) { for (int green = 0; green < LEVELS; ++ green) { g.setColor (new Color (red * 255 / (LEVELS - 1), green * 255 / (LEVELS - 1), b)); g.fillRect (red * h, green * v, h, v); } } int x = LEVELS * h + h / 2; int y = v / 2 + v * (b * (LEVELS - 1) / 255); g.setColor (getForeground ()); g.drawLine (x, y, x + 2 * h - 1, y); for (int blue = 0; blue < LEVELS; ++ blue) { g.setColor (new Color (0, 0, blue * 255 / (LEVELS - 1))); g.fillRect ((LEVELS + 1) * h, blue * v, h, v); } g.setColor (new Color (r, this.g, b)); g.fillRect ((LEVELS + 3) * h, 0, h * LEVELS, v * LEVELS); } 

The processMouseEvent() method is called automatically by Component's processEvent() method when a mouse event is generated. We override this method to call our own mousePressed() method for mouse-press events, and then we call the superclass processMouseEvent() to perform further appropriate processing. If there are other registered listeners for our own mouse events, the superclass method will appropriately inform them through their MouseListener interface:

/* * This code allows us to catch mouse events without registering listeners. */ protected void processMouseEvent (MouseEvent e) { if (e.getID () == MouseEvent.MOUSE_PRESSED) { mousePressed (e); } super.processMouseEvent (e); } 

We call mousePressed when the user clicks on the color picker. If the user clicks in the swatch of colors, we assign new red and green color levels, quantized to the chosen number of color levels. If the user clicks in the blue bar, we assign a new blue level. If the user clicks in the right-hand region, we call the postColorEvent() method to post an appropriate event. Let's see how this works:

public void mousePressed (MouseEvent e) { int h = getSize ().width / (LEVELS + 3 + LEVELS); int v = getSize ().height / (LEVELS); if (e.getX () < LEVELS * h) { // in swatch area r = (e.getX () / h) * 255 / (LEVELS - 1); r = (r  255) ? 255 : r; g = (e.getY () / v) * 255 / (LEVELS - 1); g = (g  255) ? 255 : g; repaint (); } else if (e.getX () < (LEVELS + 3) * h) { // in blue bar b = (e.getY () / v) * 255 / (LEVELS - 1); b = (b  255) ? 255 : b; repaint (); } else { // in select square postColorEvent (); } } 

Now take a look at the following snippet:

/* * This code posts a new ColorEvent to the system event queue. */ protected void postColorEvent () { ColorEvent e = new ColorEvent (this, new Color (r, g, b)); Toolkit toolkit = getToolkit (); EventQueue queue = toolkit.getSystemEventQueue (); queue.postEvent (e); } // alternatively: // dispatchEvent (new ColorEvent (this, new Color (r, g, b))); 

The postColorEventmetode menciptakan baru ColorEventdengan thissebagai asal-usul dan warna yang dipilih saat ini sebagai muatan, dan posting ke sistem antrian acara. Di bawah JDK 1.0.2 kami memanggil postEvent()metode untuk menyebabkan peristiwa meresap ke atas hierarki kontainer. Di bawah JDK 1.1 kita dapat memanggil dispatchEvent()untuk segera mengirimkan suatu peristiwa atau kita dapat mengirim peristiwa ke antrian peristiwa sistem. Antrian kejadian ini dipantau oleh utas AWT ( EventDispatchThread), yang hanya mengekstrak AWTEvents dan panggilan dispatchEvent()pada komponen yang merupakan sumber kejadian.