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

Συμβουλή Java 142: Pushing JButtonGroup

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

Σημείωση: Μπορείτε να κατεβάσετε τον πηγαίο κώδικα αυτού του άρθρου από τους πόρους.

Τρύπες Ομάδας Κουμπιών

Ακολουθεί ένα κοινό σενάριο στην ανάπτυξη Swing GUI: Δημιουργείτε μια φόρμα για τη συλλογή δεδομένων σχετικά με στοιχεία που κάποιος θα εισάγει σε μια βάση δεδομένων ή θα αποθηκεύσει σε ένα αρχείο. Η φόρμα μπορεί να περιέχει πλαίσια κειμένου, πλαίσια ελέγχου, κουμπιά επιλογής και άλλα widget. Χρησιμοποιείτε το Ομάδα κουμπιών τάξη για να ομαδοποιήσετε όλα τα κουμπιά επιλογής που χρειάζονται μία επιλογή. Όταν η σχεδίαση φόρμας είναι έτοιμη, αρχίζετε να εφαρμόζετε τα δεδομένα φόρμας. Αντιμετωπίζετε το σύνολο των κουμπιών επιλογής και πρέπει να ξέρετε ποιο κουμπί στην ομάδα επιλέχθηκε, ώστε να μπορείτε να αποθηκεύσετε τις κατάλληλες πληροφορίες στη βάση δεδομένων ή το αρχείο. Είσαι πλέον κολλημένος. Γιατί; ο Ομάδα κουμπιών Το μάθημα δεν σας δίνει αναφορά στο κουμπί που έχει επιλεγεί αυτήν τη στιγμή στην ομάδα.

Ομάδα κουμπιών έχει ένα getSelection () μέθοδος που επιστρέφει το μοντέλο του επιλεγμένου κουμπιού (ως ButtonModel τύπος), όχι το ίδιο το κουμπί. Τώρα, αυτό μπορεί να είναι εντάξει αν μπορούσατε να λάβετε την αναφορά κουμπιού από το μοντέλο του, αλλά δεν μπορείτε. ο ButtonModel η διεπαφή και οι κλάσεις εφαρμογής της δεν σας επιτρέπουν να ανακτήσετε μια αναφορά κουμπιού από το μοντέλο της. Λοιπόν τι κάνεις? Κοιτάς το Ομάδα κουμπιών τεκμηρίωση και δείτε το getActionCommand () μέθοδος. Θυμάσαι ότι αν δημιουργείς ένα JRadioButton με Σειρά για το κείμενο που εμφανίζεται δίπλα στο κουμπί και μετά καλείτε getActionCommand () στο κουμπί, το κείμενο στον κατασκευαστή επιστρέφει. Ίσως πιστεύετε ότι μπορείτε να συνεχίσετε με τον κωδικό γιατί ακόμα κι αν δεν έχετε το κουμπί αναφοράς τουλάχιστον έχετε το κείμενό του και εξακολουθείτε να γνωρίζετε το επιλεγμένο κουμπί.

Λοιπόν, έκπληξη! Ο κωδικός σας σπάει κατά το χρόνο εκτέλεσης με ένα NullPointerException. Γιατί; Επειδή getActionCommand () σε ButtonModel επιστρέφει μηδενικό. Εάν στοιχηματίσετε (όπως εγώ) getActionCommand () παράγει το ίδιο αποτέλεσμα είτε καλείται στο κουμπί είτε στο μοντέλο (όπως συμβαίνει με το Πολλά άλλες μεθόδους, όπως είναιΕπιλεγμένο (), είναι ενεργοποιημένο(), ή getMnemonic ()), έχασες. Εάν δεν καλέσετε ρητά setActionCommand () στο κουμπί, δεν ορίζετε την εντολή ενέργειας στο μοντέλο της και επιστρέφεται η μέθοδος λήψης μηδενικό για το μοντέλο. Ωστόσο, η μέθοδος λήψης κάνει επιστρέψτε το κείμενο του κουμπιού όταν σας ζητηθεί. Εδώ είναι το getActionCommand () μέθοδος στο Περίληψη Κουμπί, κληρονομείται από όλες τις τάξεις κουμπιών στο Swing:

 δημόσια συμβολοσειρά getActionCommand () {String ac = getModel (). getActionCommand (); αν (ac == null) {ac = getText (); } επιστροφή ac; } 

Αυτή η ασυνέπεια στη ρύθμιση και τη λήψη της εντολής δράσης είναι απαράδεκτη. Μπορείτε να αποφύγετε αυτήν την κατάσταση εάν σύνολο κειμένου () σε Περίληψη Κουμπί ορίζει την εντολή δράσης του μοντέλου στο κείμενο του κουμπιού όταν η εντολή δράσης είναι μηδενική. Μετά από όλα, εκτός αν setActionCommand () καλείται ρητά με μερικούς Σειρά όρισμα (όχι null), το κείμενο του κουμπιού είναι θεωρείται η εντολή δράσης από το ίδιο το κουμπί. Γιατί το μοντέλο πρέπει να συμπεριφέρεται διαφορετικά;

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

  • Κλήση getElements () επί Ομάδα κουμπιών, που επιστρέφει ένα Απαρίθμηση
  • Επανάληψη μέσω του Απαρίθμηση για να λάβετε μια αναφορά σε κάθε κουμπί
  • Κλήση είναιΕπιλεγμένο () σε κάθε κουμπί για να προσδιορίσετε αν έχει επιλεγεί
  • Επιστρέψτε μια αναφορά στο κουμπί που επέστρεψε true
  • Ή, εάν χρειάζεστε την εντολή δράσης, καλέστε getActionCommand () στο κουμπί

Εάν αυτό μοιάζει με πολλά βήματα μόνο για να πάρετε μια αναφορά κουμπιού, διαβάστε παρακάτω. πιστεύω Ομάδα κουμπιώνΗ εφαρμογή είναι θεμελιωδώς λανθασμένη. Ομάδα κουμπιών διατηρεί μια αναφορά στο μοντέλο του επιλεγμένου κουμπιού όταν πρέπει να διατηρεί μια αναφορά στο ίδιο το κουμπί. Επιπλέον, από τότε getSelection () ανακτά τη μέθοδο του επιλεγμένου κουμπιού, ίσως νομίζετε ότι είναι η αντίστοιχη μέθοδος ρύθμισης setSelection (), αλλά δεν είναι: είναι σύνολοΕπιλεγμένο (). Τώρα, σύνολοΕπιλεγμένο () έχει μεγάλο πρόβλημα. Τα επιχειρήματά του είναι α ButtonModel και ένα boolean. Εάν καλέσετε σύνολοΕπιλεγμένο () πάνω σε Ομάδα κουμπιών και περάστε το μοντέλο ενός κουμπιού που δεν ανήκει στην ομάδα και αληθής ως επιχειρήματα, τότε αυτό το κουμπί επιλέγεται και όλα τα κουμπιά στην ομάδα δεν επιλέγονται. Με άλλα λόγια, Ομάδα κουμπιών έχει τη δύναμη να επιλέξει ή να αποεπιλέξει οποιοδήποτε κουμπί έχει περάσει στη μέθοδο του, παρόλο που το κουμπί δεν έχει καμία σχέση με την ομάδα. Αυτή η συμπεριφορά παρουσιάζεται επειδή σύνολοΕπιλεγμένο () σε Ομάδα κουμπιών δεν ελέγχει αν το ButtonModel αναφορά που ελήφθη ως όρισμα αντιπροσωπεύει ένα κουμπί στην ομάδα. Και επειδή η μέθοδος επιβάλλει μία επιλογή, στην πραγματικότητα αποεπιλέγει τα δικά της κουμπιά για να επιλέξει ένα που δεν σχετίζεται με την ομάδα.

Αυτή η διάταξη στο Ομάδα κουμπιών Η τεκμηρίωση είναι ακόμη πιο ενδιαφέρουσα:

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

Βασικά, όχι ακριβώς. Μπορείτε να χρησιμοποιήσετε οποιοδήποτε κουμπί, κάνοντας οπουδήποτε στην εφαρμογή σας, ορατό ή όχι, ακόμη και απενεργοποιημένο. Ναι, μπορείτε ακόμη και να χρησιμοποιήσετε την ομάδα κουμπιών για να επιλέξετε ένα απενεργοποιημένο κουμπί εκτός της ομάδας και θα αποεπιλέξει όλα τα κουμπιά της. Για να λάβετε αναφορές σε όλα τα κουμπιά της ομάδας, πρέπει να καλέσετε το γελοίο getElements (). Τι σχέση έχουν τα "στοιχεία" Ομάδα κουμπιών είναι εικασία κανενός. Το όνομα ήταν πιθανώς εμπνευσμένο από το Απαρίθμηση οι μέθοδοι της τάξης (hasMoreElements () και επόμενο στοιχείο ()), αλλά getElements () σαφώς θα έπρεπε να έχει ονομαστεί getButtons (). Ένα κουμπί ομαδοποιεί κουμπιά, όχι στοιχεία.

Λύση: JButtonGroup

Για όλους αυτούς τους λόγους ήθελα να εφαρμόσω μια νέα τάξη που θα διορθώνει τα λάθη Ομάδα κουμπιών και παρέχουν κάποια λειτουργικότητα και ευκολία στον χρήστη. Έπρεπε να αποφασίσω εάν η τάξη θα πρέπει να είναι μια νέα τάξη ή να κληρονομήσω Ομάδα κουμπιών. Όλα τα προηγούμενα επιχειρήματα προτείνουν τη δημιουργία μιας νέας τάξης και όχι ενός Ομάδα κουμπιών υποδιαίρεση τάξεως. Ωστόσο, το ButtonModel διεπαφή απαιτεί μια μέθοδο setGroup () που παίρνει ένα Ομάδα κουμπιών διαφωνία. Εκτός αν ήμουν έτοιμος να επαναλάβω τα μοντέλα κουμπιών, η μόνη μου επιλογή ήταν να υποκατηγορήσω Ομάδα κουμπιών και παρακάμπτει τις περισσότερες από τις μεθόδους του. Μιλώντας για το ButtonModel διεπαφή, παρατηρήστε την απουσία μιας μεθόδου που ονομάζεται getGroup ().

Ένα άλλο ζήτημα που δεν ανέφερα είναι αυτό Ομάδα κουμπιών εσωτερικά διατηρεί αναφορές στα κουμπιά του στο α Διάνυσμα. Έτσι, παίρνει άσκοπα το συγχρονισμένο Διάνυσμαείναι γενικά, όταν πρέπει να χρησιμοποιεί ένα Λίστα Array, δεδομένου ότι η ίδια η τάξη δεν είναι ασφαλής στο νήμα και έτσι το Swing είναι μονό νήμα. Ωστόσο, η προστατευμένη μεταβλητή κουμπιά δηλώνεται α Διάνυσμα τύπος και όχι Λίστα όπως θα περίμενε κανείς καλό στυλ προγραμματισμού. Έτσι, δεν θα μπορούσα να επαναλάβω την εφαρμογή της μεταβλητής ως Λίστα Array; και επειδή ήθελα να τηλεφωνήσω προσθέτω επι πλέον() και super.remove (), Δεν μπόρεσα να κρύψω τη μεταβλητή superclass. Έτσι εγκατέλειψα το ζήτημα.

Προτείνω την τάξη JButtonGroup, με τόνο με τα περισσότερα ονόματα της τάξης Swing. Η τάξη παρακάμπτει τις περισσότερες μεθόδους στο Ομάδα κουμπιών και παρέχει πρόσθετες μεθόδους ευκολίας. Διατηρεί μια αναφορά στο τρέχον επιλεγμένο κουμπί, στο οποίο μπορείτε να ανακτήσετε με μια απλή κλήση getSelected (). Χάρη σε Ομάδα κουμπιώνΗ κακή εφαρμογή, θα μπορούσα να ονομάσω τη μέθοδο μου getSelected (), Από getSelection () είναι η μέθοδος που επιστρέφει το μοντέλο κουμπιού.

Παρακάτω είναι JButtonGroupοι μέθοδοι.

Πρώτον, έκανα δύο τροποποιήσεις στο Προσθήκη() μέθοδος: Εάν το κουμπί που θα προστεθεί είναι ήδη στην ομάδα, η μέθοδος επιστρέφει. Έτσι, δεν μπορείτε να προσθέσετε ένα κουμπί σε μια ομάδα περισσότερες από μία φορές. Με Ομάδα κουμπιών, μπορείτε να δημιουργήσετε ένα JRadioButton και προσθέστε το 10 φορές στην ομάδα. Κλήση getButtonCount () θα επιστρέψει στη συνέχεια 10. Αυτό δεν πρέπει να συμβεί, επομένως δεν επιτρέπω διπλές αναφορές. Στη συνέχεια, εάν το κουμπί προσθήκης είχε προηγουμένως επιλεγεί, γίνεται το επιλεγμένο κουμπί (αυτή είναι η προεπιλεγμένη συμπεριφορά στο Ομάδα κουμπιών, το οποίο είναι λογικό, οπότε δεν το παρακάμψαμε). ο επιλεγμένο κουμπί Η μεταβλητή είναι μια αναφορά στο τρέχον επιλεγμένο κουμπί στην ομάδα:

public void add (κουμπί AbstractButton) κουμπιά. περιέχει (κουμπί)) επιστροφή; super.add (κουμπί); εάν (getSelection () == button.getModel ()) επιλεγμένο Button = button; 

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

public void add (κουμπιά AbstractButton []) {if (κουμπιά == null) επιστροφή; για (int i = 0; i

Οι ακόλουθες δύο μέθοδοι αφαιρούν ένα κουμπί ή μια σειρά κουμπιών από την ομάδα:

public void remove (κουμπί AbstractButton) {if (κουμπί! = null) {if (επιλεγμένο κουμπί == κουμπί) επιλεγμένο κουμπί = null; super.remove (κουμπί); }} δημόσια αφαίρεση κενού (κουμπιά AbstractButton []) {if (κουμπιά == null) επιστροφή; για (int i = 0; i

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

public void setSelected (κουμπί AbstractButton, επιλεγμένο boolean) {if (κουμπί! = null && keys.contains (κουμπί)) {setSelected (button.getModel (), επιλεγμένο); εάν (getSelection () == button.getModel ()) επιλεγμένο Button = button; }} public void setSelected (μοντέλο ButtonModel, επιλεγμένο boolean) {AbstractButton button = getButton (model); εάν (κουμπιά. Περιέχει (κουμπί)) super.setSelected (μοντέλο, επιλεγμένο); } 

ο getButton () Η μέθοδος ανακτά μια αναφορά στο κουμπί του οποίου δίνεται το μοντέλο. σύνολοΕπιλεγμένο () χρησιμοποιεί αυτήν τη μέθοδο για να ανακτήσει το κουμπί που θα επιλεγεί δεδομένου του μοντέλου του. Εάν το μοντέλο που μεταβιβάστηκε στη μέθοδο ανήκει σε ένα κουμπί εκτός της ομάδας, μηδενικό επιστρέφεται. Αυτή η μέθοδος πρέπει να υπάρχει στο ButtonModel υλοποιήσεις, αλλά δυστυχώς δεν:

δημόσια AbstractButton getButton (μοντέλο ButtonModel) {Iterator it = buttons.iterator (); ενώ (it.hasNext ()) {AbstractButton ab = (AbstractButton) it.next (); εάν (ab.getModel () == model) επιστρέψτε ab; } επιστροφή μηδέν; } 

getSelected () και είναιΕπιλεγμένο () είναι οι απλούστερες και πιθανώς πιο χρήσιμες μέθοδοι του JButtonGroup τάξη. getSelected () επιστρέφει μια αναφορά στο επιλεγμένο κουμπί και είναιΕπιλεγμένο () υπερφορτώνει τη μέθοδο με το ίδιο όνομα στο Ομάδα κουμπιών για να πάρετε μια αναφορά κουμπιού:

δημόσιο AbstractButton getSelected () {return selectButton; } δημόσιο boolean isSelected (κουμπί AbstractButton) {κουμπί επιστροφής == επιλεγμένο κουμπί; } 

Αυτή η μέθοδος ελέγχει εάν ένα κουμπί ανήκει στην ομάδα:

Το δημόσιο boolean περιέχει (κουμπί AbstractButton) {return keys.contains (κουμπί); } 

Θα περιμένατε μια μέθοδο που ονομάζεται getButtons () σε ένα Ομάδα κουμπιών τάξη. Επιστρέφει μια αμετάβλητη λίστα που περιέχει αναφορές στα κουμπιά της ομάδας. Η αμετάβλητη λίστα αποτρέπει την προσθήκη ή την αφαίρεση κουμπιών χωρίς να περάσει από τις μεθόδους της ομάδας κουμπιών. getElements () σε Ομάδα κουμπιών όχι μόνο έχει ένα εντελώς ανεπιθύμητο όνομα, αλλά επιστρέφει ένα Απαρίθμηση, η οποία είναι μια ξεπερασμένη τάξη που δεν πρέπει να χρησιμοποιείτε. Το Συλλογές Πλαίσιο παρέχει όλα όσα χρειάζεστε για να αποφύγετε απαριθμήσεις. Ετσι getButtons () επιστρέφει μια αμετάβλητη λίστα:

δημόσια λίστα getButtons () {return Collections.unmodifiableList (κουμπιά); } 

Βελτιώστε το ButtonGroup

ο JButtonGroup Η τάξη προσφέρει μια καλύτερη και πιο βολική εναλλακτική λύση στο Swing Ομάδα κουμπιών τάξη, διατηρώντας παράλληλα όλη τη λειτουργικότητα του superclass.

Ο Daniel Tofan είναι μεταδιδακτορικός συνεργάτης στο Τμήμα Χημείας του State University of New York, Stony Brook. Το έργο του περιλαμβάνει την ανάπτυξη του πυρήνα ενός συστήματος διαχείρισης μαθημάτων με εφαρμογή στη χημεία. Είναι Sun Certified Programmer για την Java 2 Platform και κατέχει διδακτορικό στη χημεία.

Μάθετε περισσότερα σχετικά με αυτό το θέμα

  • Κατεβάστε τον πηγαίο κώδικα που συνοδεύει αυτό το άρθρο

    //images.techhive.com/downloads/idge/imported/article/jvw/2003/09/jw-javatip142.zip

  • Αρχική σελίδα Java Foundation Classes της Sun Microsystems

    //java.sun.com/products/jfc/

  • Java 2 Platform, Standard Edition (J2SE) 1.4.2 API τεκμηρίωση

    //java.sun.com/j2se/1.4.2/docs/api/

  • Κατηγορία ButtonGroup

    //java.sun.com/j2se/1.4.2/docs/api/javax/swing/ButtonGroup.html

  • Δείτε όλα τα προηγούμενα Συμβουλές Java και υποβάλετε το δικό σας

    //www.javaworld.com/columns/jw-tips-index.shtml

  • Περιηγηθείτε στο AWT / Swing τμήμα του JavaWorld 'Τοπ Ευρετήριο

    //www.javaworld.com/channel_content/jw-awt-index.shtml

  • Περιηγηθείτε στο Μαθήματα ιδρύματος τμήμα του JavaWorld 'Τοπ Ευρετήριο

    //www.javaworld.com/channel_content/jw-foundation-index.shtml

  • Περιηγηθείτε στο Σχεδιασμός διεπαφής χρήστη τμήμα του JavaWorld 'Τοπ Ευρετήριο

    //www.javaworld.com/channel_content/jw-ui-index.shtml

  • Επισκεφτείτε το φόρουμ JavaWorld

    //www.javaworld.com/javaforums/ubbthreads.php?Cat=&C=2

  • Εγγραφείτε JavaWorld 'δωρεάν εβδομαδιαία ενημερωτικά δελτία email

    //www.javaworld.com/subscribe

Αυτή η ιστορία, "Java Tip 142: Pushing JButtonGroup" δημοσιεύθηκε αρχικά από την JavaWorld.

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