30.12. Chapter Summary

 
[Page 902 ( continued )]

27.5. Creating Custom Source Components

You have used source components such as JButton . This section demonstrates how to create a custom source component.

A source component must have the appropriate registration and deregistration methods for adding and removing listeners. Events can be unicasted (only one listener object is notified of the event) or multicasted (each object in a list of listeners is notified of the event). The naming pattern for adding a unicast listener is


[Page 903]
   public void   add<  Event  >Listener(<  Event  >Listener l)   throws   TooManyListenersException; 

The naming pattern for adding a multicast listener is the same, except that it does not throw the TooManyListenersException .

   public void   add<  Event  >Listener(<  Event  >Listener l) 

The naming pattern for removing a listener (either unicast or multicast) is:

   public void   remove<  Event  >Listener(<  Event  >Listener l) 

A source component contains the code that creates an event object and passes it to the listening components by calling a method in the listener's event listener interface. You may use a standard Java event class like ActionEvent to create event objects or may define your own event classes if necessary.

The Course class in §7.16, "Case Study: The Course Class," models the courses. Suppose a Course object fires an ActionEvent when the number of students for the course exceeds a certain enrollment cap. The new class named CourseWithActionEvent is shown in Figure 27.5.

Figure 27.5. The new CourseWithActionEvent class can fire an ActionEvent .

The source component is responsible for registering listeners, creating events, and notifying listeners by invoking the methods defined in the listeners' interfaces. The CourseWithActionEvent component is capable of registering multiple listeners, generating ActionEvent objects when the enrollment exceeds the cap, and notifying the listeners by invoking the listeners' actionPerformed method. Listing 27.2 implements the new class.

Listing 27.2. CourseWithActionEvent.java
(This item is displayed on pages 903 - 905 in the print version)
 1   import   java.util.*; 2   import   java.awt.event.*; 3 

[Page 904]
 4   public class   CourseWithActionEvent { 5   private   String name =   "default name"   ; 6   private  ArrayList<String> students =   new   ArrayList<String>();  7   private int   enrollmentCap =   10   ; 8 9    private   ArrayList<ActionListener> actionListenerList;  10 11   public   CourseWithActionEvent() { 12 } 13 14    public   CourseWithActionEvent(String name) {  15   this   .name = name; 16 } 17 18   public void   addStudent(String student) { 19 students.add(student); 20 21   if   (students. size () > enrollmentCap) { 22  // Fire ActionEvent  23 processEvent(   new   ActionEvent(   this   , 24 ActionEvent.ACTION_PERFORMED,   null   )); 25 } 26 } 27 28   public   String[] getStudents() { 29   return   (String[])students.toArray(); 30 } 31 32   public int   getNumberOfStudents() { 33   return   students.size(); 34 } 35 36   public int   getEnrollmentCap() { 37   return   enrollmentCap; 38 } 39 40   public void   setEnrollmentCap(   int   enrollmentCap) { 41   this   .enrollmentCap = enrollmentCap; 42 } 43 44  /** Register an action event listener */  45    public synchronized void   addActionListener  46 (ActionListener listener) { 47   if   (actionListenerList ==   null   ) { 48 actionListenerList =   new   ArrayList<ActionListener>(   2   ); 49 } 50 51   if   (!actionListenerList.contains(listener)) { 52 actionListenerList.add(listener); 53 } 54 } 55 56  /** Remove an action event listener */  57    public synchronized void   removeActionListener  58 (ActionListener listener) { 59   if   (actionListenerList != 60   null   && actionListenerList.contains(listener)) { 61 actionListenerList.remove(listener); 62 } 63 } 64 

[Page 905]
 65  /** Fire ActionEvent */  66    private void   processEvent(ActionEvent e) {  67 ArrayList list; 68 69   synchronized   (   this   ) { 70   if   (actionListenerList ==   null   )   return   ; 71 list = (ArrayList)actionListenerList.clone(); 72 } 73 74   for   (   int   i =     ; i < list.size(); i++) { 75 ActionListener listener = (ActionListener)list.get(i); 76 listener.actionPerformed(e); 77 } 78 } 79 } 

Since the source component is designed for multiple listeners, a java.util.ArrayList instance actionListenerList is used to hold all the listeners for the source component (line 9). The data type of the elements in the array list is ActionListener . To add a listener, listener , to actionListenerList , use

 actionListenerList.add(listener); (line   52   ) 

To remove a listener, listener , from actionListenerList , use

 actionListenerList.remove(listener); (line   61   ) 

The if statement (lines 47 “49) ensures that the addActionListener method does not add the listener twice if it is already in the list. The removeActionListener method removes a listener if it is in list. actionListenerList is an instance of ArrayList , which functions as a flexible array that can grow or shrink dynamically. Initially, actionListenerList is new ArrayList(2) (line 48), which implies that the capacity of the list is 2, but the capacity can be changed dynamically. If more than two listeners are added to actionListenerList , the list size will be automatically increased.

Note

Instead of using ArrayList , you can also use javax.swing.event.EventListenerList to store listeners. Using EventListenerList is preferred, since it provides the support for synchronization and is efficient in the case of no listeners.


The addActionListener and removeActionListener methods are synchronized to prevent data corruption on actionListenerList when attempting to register multiple listeners concurrently (lines 45, 57).

The addStudent method adds a new student to the course and checks whether the number of students is more than the enrollment cap. If so, it creates an ActionEvent and invokes the processEvent method to process the event (lines 23 “24).

The UML diagram for ActionEvent is shown in Figure 27.2. To create an ActionEvent , use the constructor

 ActionEvent(Object source, int id, String command) 

where source specifies the source component, id identifies the event, and command specifies a command associated with the event. Use ActionEvent.ACTION_PERFORMED for the id . If you don't want to associate a command with the event, use null .

The processEvent method (lines 66 “78) is invoked when an ActionEvent is generated. This notifies the listeners in actionListenerList by calling each listener's actionPerformed method to process the event. It is possible that a new listener may be added or an existing listener may be removed when processEvent is running. To avoid corruption on actionListenerList , a clone list of actionListenerList is created for use to notify listeners. To avoid corruption when creating the clone, invoke it in a synchronized block, as in lines 69 “72:


[Page 906]
   synchronized   (   this   ) {   if   (actionListenerList ==   null   )   return   ; list = (ArrayList)actionListenerList.clone(); } 

Listing 27.3 gives a test program that creates a course using the new class (line 5), sets the enrollment cap to 2 (line 8), registers a listener (line 10), and adds three students to the course (lines 11 “13). When line 13 is executed, the addStudent method adds student Tim to the course and fires an ActionEvent because the course exceeds the enrollment cap. The course object invokes the listener's actionPerformed method to process the event and displays a message Enrollment cap exceeded .

Listing 27.3. TestCourseWithActionEvent.java
 1   import   java.awt.event.*; 2 3   public class   TestCourseWithActionEvent { 4 CourseWithActionEvent course = 5    new   CourseWithActionEvent(   "Java Programming"   );  6 7   public   TestCourseWithActionEvent() { 8 course.setEnrollmentCap(   2   ); 9  ActionListener listener =   new   Listener();  10 course.addActionListener(listener); 11 course.addStudent(   "John"   ); 12 course.addStudent(   "Jim"   ); 13 course.addStudent(   "Tim"   ); 14 } 15 16   public static void   main(String[] args) { 17   new   TestCourseWithActionEvent(); 18 } 19 20    private class   Listener   implements   ActionListener {  21   public void   actionPerformed(ActionEvent e) { 22 System.out.println(   "Enrollment cap exceeded"   ); 23 } 24 } 25 } 

The flow of event processing from the source to the listener is shown in Figure 27.6.

Figure 27.6. The listener is registered with the source course , and the source invokes the listener's handler actionPerformed to process the event.
(This item is displayed on page 907 in the print version)

 


Introduction to Java Programming-Comprehensive Version
Introduction to Java Programming-Comprehensive Version (6th Edition)
ISBN: B000ONFLUM
EAN: N/A
Year: 2004
Pages: 503

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net