Java核心技术:卷1笔记[6] 事件处理

时间:2022-12-25 14:56:20
 

1.AWT中事件处理机制概览:

       1)一个监听器对象是一个实现了专门的监听器接口的类的实例;

       2)一个事件源是一个能够注册监听器对象并向它们发送事件对象的对象;

       3)事件发生时,事件源会把事件对象发送给所有的注册监听器;

       4)监听器对象随后会使用事件对象中的信息来决定对事件的反应。

2.习惯使用内部类:

       button.addActionListener(new ActionListener(){

              public void actionPerformed(ActionEvent event){

                     String command=event.getActionCommand();

                     …

              }

       }

3.适配器类:每个具有不止一个方法的AWT监听器接口都有一个实现其所有方法,但方法中什么也不做的适配器类。如WindowListener接口对应有WindowAdapter类。这意味着适配器类自动满足了Java的实现相关监听器接口的技术要求。可以通过扩展适配器类为接口中的特定事件类型指定所希望的反应,而不是去实现接口中的全部方法。

4.尽管javax.Swing.event包中包含很多Swing组件专用的监听器接口,但是对于通常的事件处理,它仍然使用基本的AWT监听器接口。

5.AWT明确区分了语义事件和低层事件,语义事件是用于表达用户操作的事件,低层事件是使这些成为可能的事件。java.awt.event包中有四个语义事件类:ActionEvent、AdjustmentEvent、ItemEvent、TextEvent;

有六个低层事件类:ComponentEvent、KeyEvent、MouseEvent、MouseWheelEvent、FocusEvent、WindowEvent、ContainerEvent。

6.Java明确区分了字符和虚拟键代码。虚拟键代码用VK_来表示,比如VK_A、VK_SHIFT。假设用户按下shift键的同时再去按A键,那么相应这个用户动作Java将会生成5个事件:

       1)按SHIFT键(为VK_SHIFT调用keyPressed)

       2)按A键(为VK_A调用keyPressed)

       3)键入“A”(为“A”调用keyTyped)

       4)释放A键(为VK_A调用keyReleased)

       5)释放SHIFT(为VK_SHIFT调用keyReleaseed)

keyTyped将keyPressed和keyReleased结合起来,它报告用户击键所生成的字符,而keyPressed和keyReleased则报告用户实际按下的键。

7.测试用户是否按下了SHIFT和右箭头键:

       puclic void keyPressed(KeyEvent event)

       {

              int keyCode = event.getKeyCode();

              if(keyCode == KeyEvent.VK_RIGHT && event.isShiftDown())

              {

                     …

              }

       }

8.并不是每次击键都会keyTyped方法捕获,只有那些能够生成Unicode字符的击键动作才会被捕获。在keyTyped方法中可以通过调用getKeyChar方法得到实际键入的字符。

9.鼠标操作会被不同的用户界面组件内部处理并翻译成相应的语义事件。

10.当用户点击了鼠标按钮时会调用下面三个监听器方法:mousePressed、mouseReleased、mouseClicked。如果只对完成点击感兴趣,可以忽略前两个方法,通过在MouseEvent参数上调用getX和getY能够得到鼠标点击时的坐标;

11.如果要区分单击、双击或者三击,需要使用getClickCount方法。

12.getModifiersEx方法能够准确报告鼠标事件中鼠标按键和键盘修饰符的值,在Windows下鼠标右键的掩码为:BUTTON3_DOWN_MASK,所以可以这样来监测鼠标右键是否按下:

       if( (event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) !=0 )

13.改变鼠标指针形状:

       setCursor(Cursor.getDefaultCursor());

       setCursor(Cursor.getPredefinedCursor(Cursor.CRODDHAIR_CURSOR));

14.定义自己的光标类型:

       Toolkit tk = Toolkit.getDefaultToolkit();

       Image img = tk.getImage(“cursor.gif”);

       Cousor newCousor = tk.createCustomCursor(img,new Point(10,10), “new cousor”);

15.如果用户在移动鼠标时按下一个鼠标按钮,则会调用mouseDragged而非mouseClicked方法。只有鼠标停留在组件中mouseMoved方法才会被调用,而mouseDragged方法在即使鼠标被拖动到组件之外时还是会被调用。

16.同一时刻,一个窗口中最多只能有一个组件拥有焦点。组件获得焦点的方法有两个:用户使用鼠标点击该组件;或者用户使用TAB键在各个组件间轮流切换焦点。

17.有些组件,如面板,在默认情况下是不接受焦点的,但默认情况可以覆盖进行修改,如:panel.setFocusable(true);

18.焦点窗口和活动窗口通常是相同的,两者不同的情况只发生在焦点拥有者处于没有框架修饰的顶层窗口(如弹出菜单)的时候。

19.得到焦点拥有者、焦点窗口、活动窗口:

       KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();

       Component owner= manager.getFocusOwner();

       Window focused= manager.getFocusedWindow();

       Window active= manager.getActiveWindow();

20.为了在焦点改变时获得通知,需要在组件或者窗口中安装焦点监听器。组件焦点监听器必须实现FocusListener接口,这个接口有两个方法:focusGained和focusLost。两个方法都有一个FocusEvent参数。

21. FocusEvent的isTemporary方法在发生临时性焦点改变时会返回true,当一个组件暂时失去控制但会自动重新获得时,就会发生临时性的焦点改变。比如用户选择了其他的活动窗口,一旦用户重新选择了当前窗口,原来那个组件将重新获得焦点。

22.要获得窗口焦点事件,需要为窗口安装WindowFocusListener,并实windowGainedFocus和windowLostFocus方法。

23.当组件或窗口丢失焦点时,对端指的是获得焦点的组件或窗口;在组件或窗口获得焦点时,对端则是指丢失焦点的组件或窗口。FocusEvent类的getOppositeComponent方法用于报告对端组件,WindowEvent类的getOppositeWindow用于报告对端窗口。

24.可以使用Component类的requestFocus或requestFocusInWindow方法来把焦点移动到其他组件上。后者只在组件位于焦点窗口中时才成功,而前者对于没有包含在当前焦点窗口中的组件执行成功与否取决于平台。

25.不应该在requestFocus或requestFocusInWindow方法返回true的时候就认定组件已经获得了焦点,而是应该等待FOCUS_GAINED事件的发送。

26.Action接口是Swing包提供的一种机制,用来封装命令并把它们连接到多个事件源。Action接口扩展了ActionListener接口,Action接口具有如下方法:

       void addPropertyChangeListener(PropertyChangeListener listener) 添加一个 PropertyChange 侦听器。

      Object getValue(String key)  使用关联的键获取此对象的一个属性。

      boolean isEnabled()  返回 Action 的启用状态。

      void putValue(String key, Object value)  使用关联的键设置此对象的一个属性。

      void removePropertyChangeListener(PropertyChangeListener listener)移除一个 PropertyChange 侦听器。

      void setEnabled(boolean b)  设置 Action 的启用状态。

27. 一个动作就是一个对象,它封装了:命令的说明、执行命令需要的参数。当一个动作被连接到菜单项或工具栏按钮,但是动作被禁止时,那么菜单项或者按钮会变为灰色。

28.用预定义动作名把动作名字和图标存储到动作对象中:

       action.putValue(Action.NAME,”blue”);

       action.putValue(Action.SMALL_ICON,new ImageIcon(“blue-ball.gif”));

       如果动作对象被添加到菜单或者工具栏中,那么它的名字和图标会自动地显示在菜单项或者工具栏按钮上。

29.有一个类实现了Action接口中除了第一个方法以外的所有方法,即AbstractAction类。

30.JButton类有一个构造器使用Action对象作为参数:

       public class ColorAction extends AbstractAction…

       Action blueAction = new ColorAction(“Blue”,new ImageIcon(“blue-ball.gif”),Color.BLUE);

       JButton blueButton = new JButton(blueAction);

       构造器将读取动作中对命令的说明(字符串或可选图标)并进行相应的设置。

31.要把动作关联到击键,首先需要生成一个KeyStroke类的对象。KeyStroke类封装了对键的说明,可以使用KeyStroke类的静态方法getKeyStroke来生成一个KeyStroke对象。

       可以指定虚拟键代码和标志来生成:

              KeyStroke ctrlBKey = KeyStroke.getKeyStroke(KeyEvent.VK_B,Event.CTRL_MASK);

       或者使用字符串来说明一次击键:

              KeyStroke ctrlBKey = KeyStroke.getKeyStroke(“ctrl B”);

32.每个JComponent都有三个输入映射来把KeyStroke对象映射到动作,这三个输入映射对应着三个不同的条件:

       WHEN_ANCESTOR_OF_FOCUSED_COMPONENT :用于 registerKeyboardAction 的常量,意味着当接收组件是获得焦点的组件的祖先或者其本身就是获得焦点的组件时,应该调用命令。

       WHEN_FOCUSED :用于 registerKeyboardAction 的常量,意味着在组件获得焦点时应该调用命令。

       WHEN_IN_FOCUSED_WINDOW:用于 registerKeyboardAction 的常量,意味着当接收组件处于获得焦点的窗口内或者其本身就是获得焦点的组件时,应该调用命令。

       击键处理需要按照以下顺序来检查映射:

       1)检查具有输入焦点的组件的WHEN_FOCUSED映射,如果该击键存在,那么执行相应的动作。如果该动作已启动,那么停止处理;

       2)从具有输入焦点的组件开始,检查它父组件的WHEN_ANCESTOR_OF_FOCUSED_COMPONENT映射,如果找到击键对应的映射,则执行相应的动作,如果动作已启用,那么停止处理;

       3)遍历具有输入焦点的窗口中的所有可视和已启用的组件,它们把这个击键注册到WHEN_IN_FOCUSED_WINDOW映射中。给这些组件(按照它们的击键注册顺序)一个机会来执行相应的动作。只要第一个启用的动作被执行了,那么停止处理。如果一个击键在多个WHEN_IN_FOCUSED_WINDOW映射出现,处理过程的这一部分会有些脆弱。

33.可以使用getInputMap方法从组件中得到输入映射,如:

       InputMap imap = panel.getInputMap(JComponent.WHEN_FOCUSED);

34.InputMap不直接把KeyStroke对映射到Action对象上,而是将其映射到一个临时对象上,然后第二个对象(由ActionMap类实现)把临时对象映射到动作。这使得很容易让来自不同输入映射的击键可以共享一个动作。

35.把一个键绑定到一个动作:

       InputMap imap = panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

       Action yellowAction = new ColorAction(“yellow”,new ImageIcon(“yellow-ball.gif”),Color.YELLOW);

       imap.put(KeyStroke.getKeyStroke(“ctrl Y”),”panel.yellow”);

       ActionMap amap = panel.getActionMap();

       amap.put(“panel.yellow”,yellowAction);

       空动作可以使用字符串”none”来表示,所以很容易取消一个键的动作:

       imap.put(KeyStroke.getKeyStroke(“ctrl Y”),”none”);

36.InputMap将一个KeyStroke对应到一个对象,ActionMap将一个对象对应到一个行为。通常InputMap中KeyStroke所对应的对象是一个字符串,通过这个字符串可以在ActionMap中查找到相应的行为。

       InputMap:void put(KeyStroke keyStroke, Object actionMapKey),将 keyStroke 的一个绑定添加到 actionMapKey。

       ActionMap:void put(Object key, Action action) ,添加一个 key 到 action 的绑定。

37.总结如何执行同一动作来响应按钮、菜单项或者击键:

       1)创建一个类扩展AbstractAction类;

       2)创建那个动作类的对象;

       3)使用动作对象构造按钮或者菜单项。构造器从动作对象读取标签文本和图标;

       4)对于能够通过键盘触发的动作来说,还要多做一步。先要定位窗口的顶层组件,如包含了其他所有组件的面板。

       5)得到顶层组件的WHEN_ANCESTOR_OF_FOCUSED_COMPONENT输入映射。为需要的击键创建一个KeyStroke对象。创建动作键对象,比如字符串来描述动作,增加(击键、动作键)对到输入映射中去。

       6)最后得到顶层组件的动作映射。把(动作键、动作对象)对添加进该映射中。

38.所有AWT事件源都支持一种对应监听器的多点传送模型。也即同一事件能够被传送到不止一个监听器对象。只要把多个监听器添加到一个事件源中就能够让所有注册的监听器都能够对事件作出响应。

39.得到表示事件队列的对象:

       EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();

       将新事件添加到事件队列中:

       queue.postEvent(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,”Blue”));

       使用getNextEvent方法可以删除一个事件。而peekEvent方法则返回队列中的下一个事件,但是该方法并不删除该事件。

40.定制事件的三要素:

       1)事件类型,AWT事件队列中的所有事件都必须为AWTEvent或其子类型;

              自定义事件类的实现中需要给超类一个事件ID号,程序选择的事件ID值应该大于整数常量       AWTEvent.RESERVED_ID_MAX,如下:

              class TimerEvent extends AWTEvent

              { 

                   public TimerEvent(Timer t) { super(t, TIMER_EVENT); }

                   public static final int TIMER_EVENT= AWTEvent.RESERVED_ID_MAX  + 5555;

              }

       2)事件监听器接口

              interface TimerListener extends EventListener

              { 

                   public void timeElapsed(TimerEvent event);

              }

       3)事件源,AWT事件机制要求事件源扩展Component类。

41.事件源的责任:

       1)管理它生成的事件监听器;

       2)把事件分发给向它注册的监听器;

       Swing提供类EventListenerList来简化用于添加、删除和激发事件的方法的实现。这个类会处理多线程同时添加、删除、分发事件时可能引发的细节问题。因为有些事件源接收多种类型的监听器,所以事件监听器列表中的每一个监听器都同特定的类关联起来。add和remove方法是用于实现addXXXListener及removeXXXListener方法的,例如:

        private EventListenerList listeners;

       public void addTimerListener(TimerListener listener)

   { 

      listenerList.add(TimerListener.class, listener);

   }

   public void removeTimerListener(TimerListener listener)

   { 

      listenerList.remove(TimerListener.class, listener);

   }

42.当AWT把事件从队列中删除时,它调用processEvent方法,

Component:protected  void processEvent(AWTEvent e) 处理组件上发生的事件。

例如:

       public void processEvent(AWTEvent event)

   { 

      if (event instanceof TimerEvent)

      {

         EventListener[] listeners = listenerList.getListeners(TimerListener.class);

         for (int i = 0; i < listeners.length; i++)

            ((TimerListener)listeners[i]).timeElapsed( (TimerEvent)event);

      }

      else

         super.processEvent(event);

   }