Swing組件集合的事件處理(四) – JAVA編程語言程序開發技術文章

2.2.3  管理監聽器列表
如果我們正在創建我們自己的組件並且希望這些組件觸發事件,我們需要維護一個要通知的監聽器列表。如果監聽器列表是用於AWT事件的,我們可以使用AWTEventMulticaster類用於列表管理。對於Swing庫而言,如果事件並不是一個預定義的AWT事件類型,我們需要自己管理監聽器列表。通過使用javax.swing.event包中的EventListenerList類,我們不再需要手動管理監聽器列表,也無需擔心線程安全。而且如果我們需要獲取監聽器列表,我們可以通過public EventLIstener[] getListener(Class listenerType)來請求Component,或者是類似於JButton的getActionListeners()方法的類型特定方法。這使得我們可以由一個內部管理列表中移除監聽器,從而有助於垃圾回收。


AWTEventMulticaster類


無論我們是否意識到,AWTEventMulticaster類被AWT組件用來管理事件監聽器列表。這個類實現瞭所有的AWT事件監聽器(ActionListener, AdjustmentListener, ComponentListener, ContainerListener, FocusListener, HierarchyBoundsListener, HierarchyListener, InputMethodListener, ItemListener, KeyListener, MouseListener, MouseMotionListener, MouseWheelListener, TextListener, WindowFocusListener, WindowListener以及WindowStatListener)。無論何時我們調用組件的方法來添加或是移除一個監聽器時,AWTEventMulticaster都會被用來作為支持。


如果我們希望創建我們自己的組件並且管理用於AWT事件/監聽器對的監聽器列表,我們可以使用AWTEventMulticaster。作為一個示例,我們來看一下如何創建一個通用組件,當按鍵在組件內部按下時,這個組件會生成一個ActionEvent對象。這個組件使用KeyEvent的public static String getKeyText(int keyCode)方法來將按鍵代碼轉換相應的文本字符串,並且將這個文本字符串作為ActionEvent的動作命令回傳。因為這個組件是作為ActionListener觀察者的源,他需要一對添加/移除方法來處理監聽器的註冊。這也就是AWTEventMulticaster類的用處所在,因為他會管理由我們的監聽器列表變量中監聽器的添加或移除:


private ActionListener actionListenerList = null;
public void addActionListener(ActionListener actionListener) {
  actionListenerList = AWTEventMulticaster.add(
    actionListenerList, actionListener);
}
public void removeActionListener(ActionListener actionListener) {
  actionListenerList = AWTEventMulticaster.remove(
    actionListenerList, actionListener);
}類定義的其餘部分描述瞭如何處理內部事件。為瞭向ActionListener發送擊鍵需要註冊一個內部的KeyListener。另外,組件必須能夠獲得輸入焦點;否則,所有的擊鍵都會到達其他的組件。完整的類定義如列表2-4所示。用於監聽器通知的代碼行以粗體顯示。這一行通知所有的已註冊的監聽器。


/**
 *
 */
package swingstudy.ch02;
 
import java.awt.AWTEventMulticaster;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
 
import javax.swing.JComponent;
 
/**
 * @author lenovo
 *
 */
public class KeyTextComponent extends JComponent{
 
 private ActionListener actionListenerList = null;
 
 public KeyTextComponent() {
  setBackground(Color.CYAN);
  KeyListener internalKeyListener = new KeyAdapter() {
   public void keyPressed(KeyEvent event) {
    if(actionListenerList != null) {
     int keyCode = event.getKeyCode();
     String keyText = event.getKeyText(keyCode);
     ActionEvent actionEvent = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, keyText);
     actionListenerList.actionPerformed(actionEvent);
    }
   }
  };
 
  MouseListener internalMouseListener = new MouseAdapter() {
   public void mousePressed(MouseEvent event) {
    requestFocusInWindow();
   }
  };
 
  addKeyListener(internalKeyListener);
  addMouseListener(internalMouseListener);
 }
 
 public void addActionListener(ActionListener actionListener) {
  actionListenerList = AWTEventMulticaster.add(actionListenerList, actionListener);
 }
 
 public void removeActionListener(ActionListener actionListener) {
  actionListenerList = AWTEventMulticaster.remove(actionListenerList, actionListener);
 }
 
 public boolean isFocusable() {
  return true;
 }
}圖2-5顯示所有的組件。圖中上部分是組件,而下底部則是一個文本輸入框。為瞭顯示按下鍵的文本字符串,向更新文本框的KeyTextComponent註冊瞭一個ActionListener。


示例源碼如列表2-5所示。


/**
 *
 */
package swingstudy.ch02;
 
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JFrame;
import javax.swing.JTextField;
 
/**
 * @author lenovo
 *
 */
public class KeyTextTester {
 
 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
 
  Runnable runner = new Runnable() {
   public void run() {
    JFrame frame = new JFrame(“Key Text Sample”);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
    KeyTextComponent keyTextComponent = new KeyTextComponent();
    final JTextField textField = new JTextField();
 
    ActionListener actionListener =  new ActionListener() {
     public void actionPerformed(ActionEvent event) {
      String keyText = event.getActionCommand();
      textField.setText(keyText);
     }
    };
 
    keyTextComponent.addActionListener(actionListener);
 
    frame.add(keyTextComponent, BorderLayout.CENTER);
    frame.add(textField, BorderLayout.SOUTH);
    frame.setSize(300, 200);
    frame.setVisible(true);
   }
  };
 
  EventQueue.invokeLater(runner);
 }
 
}EventListenerList類


盡管AWTEventMulticaster類很容易使用,然而他卻並不能用於管理自定義的事件監聽器列表或是javax.swing.event中的Swing事件器。我們可以創建一個這個類的自定義擴展用於處理我們需要管理的每一種類型的事件監聽器列表,或者我們可以將列表存儲在一個如Vector或是LinkedList的數據結構中。盡管使用Vector或是LinkedList可以工作得很好,當我們使用這種方法時,我們需要考慮同步問題。如果我們沒有正確的編寫列表管理,監聽器通知也許會發生錯誤的監聽器集合。


為瞭簡化這種情況,Swing組件庫包含瞭一個特殊的事件監聽呂地類,EventListenerList。這個類的一個實例可以管理一個組件的所有不同的事件監聽器。為瞭演示這個類的用法,我們來看一下如何使用EventListenerList替換AWTEventMulticaster來重寫前面的例子。註意,在這個特定例子中,使用AWTEventMulticaster類實際上是一種更為簡單的解決方法。然而,想像一個類似的情況下,在這種情況下事件監聽器並不是一個預定義的AWT事件監聽器或者是我們需要維護多個監聽器列表。


添加或是移除監聽器類似於在前面的例子中AWTEventMulticaster所用的技術。我們需要創建一個合適的變量類型-這次是EventListenerList-同時定義添加與移除監聽器方法。這兩種方法之間的主要區別在於初始的EventListenerList並不為null,而另一個初始時則是null。首先必須創建一個到空的EventListenerList的引用。這避免瞭在後面多次檢測null列表變量的需要。添加與移除監聽器的方法也有一些不同。因為EventListenerList可以管理任

發佈留言