Προγραμματισμός

Java Tip 35: Δημιουργία νέων τύπων συμβάντων στην Java

Ενώ το JDK 1.1 έχει βεβαίως βελτιώσει τον χειρισμό συμβάντων με την εισαγωγή του μοντέλου εκδήλωσης ανάθεσης, δεν διευκολύνει τους προγραμματιστές να δημιουργήσουν τους δικούς τους τύπους εκδηλώσεων. Η βασική διαδικασία που περιγράφεται εδώ είναι μάλλον απλή. Για λόγους απλότητας, δεν θα συζητήσω έννοιες σχετικά με την ενεργοποίηση εκδηλώσεων και τις μάσκες εκδηλώσεων. Επιπλέον, πρέπει να γνωρίζετε ότι τα συμβάντα που δημιουργήθηκαν με αυτήν τη διαδικασία δεν θα δημοσιεύονται στην ουρά συμβάντων και θα λειτουργούν μόνο με εγγεγραμμένους ακροατές.

Επί του παρόντος, ο πυρήνας Java αποτελείται από 12 τύπους συμβάντων που ορίζονται στο java.awt.events:

  • Δράση
  • Προσαρμογή
  • ComponentEvent
  • ContainerEvent
  • FocusEvent
  • InputEvent
  • Στοιχείο
  • KeyEvent
  • MouseEvent
  • PaintEvent
  • TextEvent
  • Παράθυρο

Επειδή η δημιουργία νέων τύπων συμβάντων είναι μια ασήμαντη εργασία, θα πρέπει να εξετάσετε συμβάντα που αποτελούν μέρος της βασικής Java. Εάν είναι δυνατόν, προσπαθήστε να χρησιμοποιήσετε αυτούς τους τύπους αντί να δημιουργήσετε νέους.

Ωστόσο, θα υπάρξουν στιγμές που θα πρέπει να αναπτυχθεί ένας νέος τύπος συμβάντος για ένα νέο στοιχείο. Για σκοπούς αυτής της συζήτησης, θα χρησιμοποιήσω το παράδειγμα ενός απλού στοιχείου, ενός πίνακα οδηγού, ως μέσο για να δείξω πώς να δημιουργήσω έναν νέο τύπο συμβάντος.

Ένας πίνακας οδηγού εφαρμόζει ένα απλό μάγος διεπαφή. Το στοιχείο αποτελείται από έναν πίνακα καρτών που μπορεί να προχωρήσει χρησιμοποιώντας το κουμπί ΕΠΟΜΕΝΟ. Το κουμπί BACK σάς επιτρέπει να μεταβείτε στον προηγούμενο πίνακα. Παρέχονται επίσης κουμπιά FINISH και CANCEL.

Για να κάνω το στοιχείο ευέλικτο, ήθελα να παρέχω πλήρη έλεγχο των ενεργειών που έγιναν από όλα τα κουμπιά στον προγραμματιστή που το χρησιμοποιεί. Για παράδειγμα, όταν πατηθεί το κουμπί ΕΠΟΜΕΝΟ, θα πρέπει να είναι δυνατό για τον προγραμματιστή να ελέγξει πρώτα εάν τα απαιτούμενα δεδομένα έχουν εισαχθεί στο στοιχείο που είναι ορατό πριν προχωρήσει στο επόμενο στοιχείο.

Υπάρχουν πέντε βασικές εργασίες για τη δημιουργία του δικού σας τύπου συμβάντος:

  • Δημιουργήστε ένα πρόγραμμα ακρόασης εκδηλώσεων

  • Δημιουργήστε έναν προσαρμογέα ακρόασης

  • Δημιουργήστε μια τάξη εκδηλώσεων

  • Τροποποιήστε το στοιχείο

  • Διαχείριση πολλαπλών ακροατών

Θα εξετάσουμε καθεμία από αυτές τις εργασίες με τη σειρά και στη συνέχεια θα τα συνδυάσουμε όλα.

Δημιουργήστε ένα πρόγραμμα ακρόασης εκδηλώσεων

Ένας τρόπος (και υπάρχουν πολλοί) να ενημερώνετε τα αντικείμενα ότι έχει συμβεί μια συγκεκριμένη ενέργεια είναι να δημιουργήσετε έναν νέο τύπο συμβάντος που θα μπορούσε να παραδοθεί σε εγγεγραμμένους ακροατές. Στην περίπτωση του πίνακα οδηγού, ένας ακροατής πρέπει να υποστηρίζει τέσσερις διαφορετικές περιπτώσεις συμβάντων, μία για κάθε κουμπί.

Ξεκινώ δημιουργώντας μια διεπαφή ακροατή. Για κάθε κουμπί, ορίζω μια μέθοδο ακρόασης με τον ακόλουθο τρόπο:

εισαγωγή java.util.EventListener; δημόσια διεπαφή Το WizardListener επεκτείνει το EventListener {public abstract void nextSelected (WizardEvent e); δημόσια περίληψη κενού backSelected (WizardEvent e); public abstract void cancelSelected (WizardEvent e). δημόσιο αφηρημένο κενό άδειοΕπιλεγμένο (WizardEvent e); } 

Κάθε μέθοδος παίρνει ένα όρισμα: WizardEvent, που ορίζεται στη συνέχεια. Σημειώστε ότι η διεπαφή επεκτείνεται EventListener, χρησιμοποιείται για την αναγνώριση αυτής της διεπαφής ως ακροατή AWT.

Δημιουργήστε έναν προσαρμογέα ακρόασης

Η δημιουργία προσαρμογέα ακρόασης είναι ένα προαιρετικό βήμα. Στο AWT, ένας προσαρμογέας ακρόασης είναι μια κλάση που παρέχει μια προεπιλεγμένη εφαρμογή για όλες τις μεθόδους ενός συγκεκριμένου τύπου ακροατή. Όλες οι τάξεις προσαρμογέα στο java.awt.event πακέτο παρέχουν κενές μεθόδους που δεν κάνουν τίποτα. Εδώ είναι μια τάξη προσαρμογέα για WizardListener:

δημόσια τάξη WizardAdapter εφαρμόζει WizardListener {public void nextSelected (WizardEvent e) {} public void backSelected (WizardEvent e) {} public void cancelSelected (WizardEvent e) {} public void finishSelected (WizardEvent e) {}} 

Όταν γράφετε μια τάξη που πρόκειται να γίνει ακροατής μάγου, είναι δυνατό να επεκτείνετε το Προσαρμογέας οδηγού και παρέχει εφαρμογή για (ή παράκαμψη) μόνο εκείνων των μεθόδων ακροατή που ενδιαφέρουν. Αυτό είναι αυστηρά ένα μάθημα ευκολίας.

Δημιουργήστε μια τάξη εκδηλώσεων

Το επόμενο βήμα είναι να δημιουργήσετε το πραγματικό Εκδήλωση τάξη εδώ: WizardEvent.

εισαγωγή java.awt.AWTEvent; δημόσια τάξη WizardEvent επεκτείνει το AWTEvent {public static final int WIZARD_FIRST = AWTEvent.RESERVED_ID_MAX + 1; δημόσιο τελικό τελικό int NEXT_SELECTED = WIZARD_FIRST; δημόσιο τελικό τελικό int BACK_SELECTED = WIZARD_FIRST + 1; δημόσιο τελικό τελικό int CANCEL_SELECTED = WIZARD_FIRST + 2; δημόσιο στατικό τελικό int FINISH_SELECTED = WIZARD_FIRST + 3; δημόσιο τελικό τελικό int WIZARD_LAST = WIZARD_FIRST + 3; public WizardEvent (πηγή οδηγού, int id) {super (πηγή, id); }} 

Δύο σταθερές, WIZARD_FIRST και WIZARD_LAST, επισημάνετε τη συνολική γκάμα μάσκας που χρησιμοποιείται από αυτήν την τάξη εκδηλώσεων. Σημειώστε ότι τα αναγνωριστικά συμβάντων χρησιμοποιούν το RESERVED_ID_MAX σταθερά τάξης ΠΡΟΣΟΧΗ για να προσδιορίσετε το εύρος των αναγνωριστικών που δεν θα έρχονται σε διένεξη με τις τιμές αναγνωριστικού συμβάντος που ορίζονται από το AWT. Καθώς προστίθενται περισσότερα στοιχεία AWT, το RESERVED_ID_MAX μπορεί να αυξηθεί στο μέλλον.

Οι υπόλοιπες τέσσερις σταθερές αντιπροσωπεύουν τέσσερα αναγνωριστικά συμβάντων, το καθένα αντιστοιχεί σε διαφορετικό τύπο ενέργειας, όπως ορίζεται από τη λειτουργικότητα του οδηγού.

Το Αναγνωριστικό συμβάντος και η πηγή συμβάντων είναι δύο ορίσματα για τον κατασκευαστή συμβάντων του οδηγού. Η πηγή συμβάντος πρέπει να είναι τύπου Μάγος - αυτός είναι ο τύπος στοιχείου για το οποίο έχει οριστεί το συμβάν. Ο λόγος είναι ότι μόνο ένας πίνακας οδηγού μπορεί να αποτελέσει πηγή συμβάντων οδηγού. Σημειώστε ότι το WizardEvent η τάξη επεκτείνεται ΠΡΟΣΟΧΗ.

Τροποποιήστε το στοιχείο

Το επόμενο βήμα είναι να εξοπλίσουμε το στοιχείο μας με μεθόδους που του επιτρέπουν να εγγράφει και να αφαιρεί ακροατές για το νέο συμβάν.

Για να παραδώσετε ένα συμβάν σε έναν ακροατή, κανονικά κάποιος θα καλούσε την κατάλληλη μέθοδο ακρόασης συμβάντων (ανάλογα με τη μάσκα συμβάντων). Μπορώ να καταχωρήσω έναν ακροατή δράσης για να λάβω συμβάντα δράσης από το κουμπί ΕΠΟΜΕΝΟ και να τα μεταφέρω στην εγγραφή WizardListener αντικείμενα. ο δράσηΠραγματοποιήθηκε Η μέθοδος του ακροατή ενεργειών για το κουμπί ΕΠΟΜΕΝΟ (ή άλλες ενέργειες) θα μπορούσε να εφαρμοστεί ως εξής:

public void actionPerformed (ActionEvent e) {// μην κάνετε τίποτα εάν δεν έχουν καταχωρηθεί ακροατές εάν (wizardListener == null) επιστρέψει. WizardEvent w; Πηγή οδηγού = αυτό; εάν (e.getSource () == nextButton) {w = νέο WizardEvent (πηγή, WizardEvent.NEXT_SELECTED); wizardListener.nextΕπιλεγμένο (w); } // χειριστείτε τα υπόλοιπα κουμπιά του οδηγού με παρόμοιο τρόπο} 

Σημείωση: Στο παραπάνω παράδειγμα, τοΜάγοςο ίδιος ο πίνακας είναι ο ακροατής για το ΕΠΟΜΕΝΟ κουμπί.

Όταν πατηθεί το κουμπί ΕΠΟΜΕΝΟ, ένα νέο WizardEvent δημιουργείται με την κατάλληλη πηγή και μάσκα που αντιστοιχεί στο κουμπί ΕΠΟΜΕΝΟ που πιέζεται.

Στο παράδειγμα, η γραμμή

 wizardListener.nextΕπιλεγμένο (w); 

αναφέρεται στο wizardListener αντικείμενο που είναι ιδιωτική μεταβλητή μέλους για Μάγος και είναι τύπου WizardListener. Ορίσαμε αυτόν τον τύπο ως το πρώτο βήμα στη δημιουργία ενός νέου συμβάντος συστατικών.

Με την πρώτη ματιά, ο παραπάνω κώδικας φαίνεται να περιορίζει τον αριθμό των ακροατών σε έναν. Η ιδιωτική μεταβλητή wizardListener δεν είναι πίνακας και μόνο ένας επόμενηΕπιλέχθηκε πραγματοποιείται κλήση. Για να εξηγήσουμε γιατί ο παραπάνω κώδικας δεν θέτει αυτόν τον περιορισμό, ας εξετάσουμε πώς προστίθενται οι ακροατές.

Κάθε νέο στοιχείο που δημιουργεί συμβάντα (προκαθορισμένα ή νέα) πρέπει να παρέχει δύο μεθόδους: μία για να υποστηρίζει την προσθήκη του ακροατή και μία για να υποστηρίζει την αφαίρεση του ακροατή. Στην περίπτωση του Μάγος τάξη, αυτές οι μέθοδοι είναι:

 δημόσιο συγχρονισμένο άκυρο addWizardListener (WizardListener l) {wizardListener = WizardEventMulticaster.add (wizardListener, l); } δημόσια συγχρονισμένη κενή αφαίρεσηWizardListener (WizardListener l) {wizardListener = WizardEventMulticaster.remove (wizardListener, l); } 

Και οι δύο μέθοδοι κάνουν μια κλήση σε στατική μέθοδο μέλη της τάξης WizardEventMulticaster.

Διαχείριση πολλαπλών ακροατών

Ενώ είναι δυνατό να χρησιμοποιήσετε ένα Διάνυσμα Για τη διαχείριση πολλαπλών ακροατών, το JDK 1.1 ορίζει μια ειδική τάξη για τη διατήρηση μιας λίστας ακροατών: AWTEventMulticaster. Μια μεμονωμένη παρουσία πολλαπλών καταστάσεων διατηρεί αναφορές σε δύο αντικείμενα ακρόασης. Επειδή το multicaster είναι επίσης ένα ίδιο το ακροατήριο (εφαρμόζει όλες τις διεπαφές ακροατή), καθένας από τους δύο ακροατές που παρακολουθεί μπορεί επίσης να είναι multicasters, δημιουργώντας έτσι μια αλυσίδα ακροατών συμβάντων ή multicasters:

Εάν ένας ακροατής είναι επίσης πολυσύχναστος, τότε αντιπροσωπεύει έναν σύνδεσμο στην αλυσίδα. Διαφορετικά, είναι απλώς ακροατής και έτσι είναι το τελευταίο στοιχείο της αλυσίδας.

Δυστυχώς, δεν είναι δυνατόν απλώς να επαναχρησιμοποιηθεί AWTEventMulticaster για να χειριστείτε την εκδήλωση multicasting για νέους τύπους συμβάντων. Το καλύτερο που μπορεί να γίνει είναι η επέκταση του AWT multicaster, αν και αυτή η λειτουργία είναι μάλλον αμφισβητήσιμη. AWTEventMulticaster περιέχει 56 μεθόδους. Από αυτές, 51 μέθοδοι παρέχουν υποστήριξη για τους 12 τύπους συμβάντων και τους αντίστοιχους ακροατές τους που αποτελούν μέρος του AWT. Εάν υποκατηγορία AWTEventMulticaster, δεν θα τα χρησιμοποιήσετε ποτέ. Από τις υπόλοιπες πέντε μεθόδους, addInternal (EventListener, EventListener), και αφαίρεση (EventListener) πρέπει να κωδικοποιηθεί. (Λέω ότι κωδικοποιήθηκε επειδή στο AWTEventMulticaster, addInternal είναι μια στατική μέθοδος και επομένως δεν μπορεί να υπερφορτωθεί. Για λόγους που δεν γνωρίζω αυτήν τη στιγμή, αφαιρώ πραγματοποιεί κλήση προς addInternal και πρέπει να υπερφορτωθεί.)

Δύο μέθοδοι, σώσει και αποθήκευση εσωτερικού, παρέχει υποστήριξη για ροή αντικειμένων και μπορεί να επαναχρησιμοποιηθεί στη νέα κλάση πολλαπλών καταστάσεων. Η τελευταία μέθοδος που υποστηρίζει ρουτίνες αφαίρεσης ακροατή, αφαίρεση εσωτερικού, μπορεί επίσης να επαναχρησιμοποιηθεί, υπό την προϋπόθεση ότι νέες εκδόσεις του αφαιρώ και addInternal έχουν εφαρμοστεί.

Για λόγους απλότητας, πρόκειται να υποκατηγορήσω AWTEventMulticaster, αλλά με πολύ λίγη προσπάθεια, είναι δυνατή η κωδικοποίηση αφαιρώ, σώσει, και αποθήκευση εσωτερικού και να έχετε μια πλήρως λειτουργική, αυτόνομη εκδήλωση multicaster.

Εδώ είναι το event multicaster όπως υλοποιήθηκε για να χειριστεί WizardEvent:

εισαγωγή java.awt.AWTEventMulticaster; εισαγωγή java.util.EventListener; δημόσια τάξη WizardEventMulticaster επεκτείνει το AWTEventMulticaster υλοποιεί το WizardListener {προστατευμένο WizardEventMulticaster (EventListener a, EventListener b) {super (a, b); } δημόσια στατική προσθήκη WizardListener (WizardListener a, WizardListener b) {return (WizardListener) addInternal (a, b); } δημόσιο στατικό WizardListener remove (WizardListener l, WizardListener oldl) {return (WizardListener) removeInternal (l, oldl); } public void nextSelected (WizardEvent e) {// εξαίρεση μετάδοσης δεν θα συμβεί ποτέ σε αυτήν την περίπτωση // απαιτείται casting _is_ επειδή αυτό το multicaster μπορεί // να χειριστεί περισσότερους από έναν μόνο ακροατές εάν (a! nextΕπιλεγμένο (ε); εάν (b! = null) ((WizardListener) b) .nextΕπιλεγμένο (e); } public void backSelected (WizardEvent e) {if (a! = null) ((WizardListener) α) .backSelected (e); εάν (b! = null) ((WizardListener) b) .backSelected (e); } public void cancelSelected (WizardEvent e) {if (a! = null) ((WizardListener) α) .cancelSelected (e); εάν (b! = null) ((WizardListener) b) .cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) α) .finishSelected (e); εάν (b! = null) ((WizardListener) b) .finishSelected (e); } προστατευμένο στατικό EventListener addInternal (EventListener a, EventListener b) {if (a == null) επιστροφή b; αν (b == null) επιστρέψτε a; επιστροφή νέου WizardEventMulticaster (a, b); } προστατευμένο EventListener remove (EventListener oldl) {if (oldl == a) επιστροφή b; εάν (oldl == b) επιστρέψτε a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); εάν (a2 == a && b2 == b) επιστρέψτε αυτό; επιστροφή addInternal (a2, b2); }} 

Μέθοδοι στην τάξη multicaster: Μια κριτική

Ας αναθεωρήσουμε τις μεθόδους που αποτελούν μέρος της παραπάνω κατηγορίας πολλαπλών παικτών Ο κατασκευαστής προστατεύεται και προκειμένου να αποκτήσει ένα νέο WizardEventMulticaster, ένα στατικό προσθήκη (WizardListener, WizardListener) πρέπει να κληθεί η μέθοδος. Χρειάζονται δύο ακροατές ως επιχειρήματα που αντιπροσωπεύουν δύο κομμάτια μιας αλυσίδας ακροατών για σύνδεση:

  • Για να ξεκινήσετε μια νέα αλυσίδα, χρησιμοποιήστε το null ως το πρώτο όρισμα.

  • Για να προσθέσετε έναν νέο ακροατή, χρησιμοποιήστε τον υπάρχοντα ακροατή ως το πρώτο όρισμα και έναν νέο ακροατή ως το δεύτερο όρισμα.

Αυτό, στην πραγματικότητα, είναι αυτό που έχει γίνει στον κώδικα για την τάξη Μάγος που έχουμε ήδη εξετάσει.

Μια άλλη στατική ρουτίνα είναι αφαίρεση (WizardListener, WizardListener). Το πρώτο επιχείρημα είναι ένας ακροατής (ή ακροατής multicaster) και ο δεύτερος είναι ένας ακροατής που πρέπει να αφαιρεθεί.

Τέσσερις δημόσιες, μη στατικές μέθοδοι προστέθηκαν για την υποστήριξη της διάδοσης συμβάντων μέσω της αλυσίδας συμβάντων. Για κάθε WizardEvent περίπτωση (δηλαδή, επόμενο, πίσω, ακύρωση και ολοκλήρωση επιλεγμένο) υπάρχει μία μέθοδος. Αυτές οι μέθοδοι πρέπει να εφαρμοστούν από το WizardEventMulticaster υλοποιεί WizardListener, το οποίο με τη σειρά του απαιτεί την παρουσία των τεσσάρων μεθόδων.

Πώς λειτουργούν όλα μαζί

Ας εξετάσουμε τώρα πώς πραγματικά χρησιμοποιείται το multicaster από το Μάγος. Ας υποθέσουμε ότι έχει κατασκευαστεί ένα αντικείμενο οδηγού και προστίθενται τρεις ακροατές, δημιουργώντας μια αλυσίδα ακροατών.

Αρχικά, η ιδιωτική μεταβλητή wizardListener της τάξης Μάγος είναι μηδενικό. Έτσι, όταν πραγματοποιείται μια κλήση WizardEventMulticaster.add (WizardListener, WizardListener), το πρώτο επιχείρημα, wizardListener, είναι μηδενικό και το δεύτερο δεν είναι (δεν έχει νόημα να προσθέσετε ένα μηδενικό ακροατή). ο Προσθήκη μέθοδος, με τη σειρά, κλήσεις addInternal. Δεδομένου ότι ένα από τα επιχειρήματα είναι άκυρο, η επιστροφή του addInternal είναι ο μηδενικός ακροατής. Η επιστροφή διαδίδεται στο Προσθήκη μέθοδος που επιστρέφει το μη μηδενικό ακροατή στο addWizardListener μέθοδος. Εκεί wizardListener η μεταβλητή έχει οριστεί στο νέο ακροατή που προστίθεται.