事件处理 - core java

来源:互联网 发布:java异常关键字怎么用 编辑:程序博客网 时间:2024/05/22 03:19

8. 事件处理 - core java


事件处理包括: 事件源类.事件类.监听器接口.监听器适配器类 它们之间的关系.
其中事件源是用户界面组件.窗口和菜单. 操作系统会把用户的动作通知给感兴趣的事件源. 事件源用一个
事件对象描述所发生事件的特性
. 并且. 事件源可以注册一组监听器. 当事件发生时会把事件对象传递给
这些监听器对象
. 由监听器对象具体处理发生的事件.

 

各种事件类的继承层次:

AWTEvent
|
|-ActionEvent
|-AdjustmentEvent
|-ItemEvent
|-TextEvent
|-ComponentEvent
   |-ContainerEvent
   |-FocusEvent
   |-PaintEvent
   |-WindowEvent
   |-InputEvent
      |-KeyEvent
      |-MouseEvent
         |-MouseWheelEvent

每个事件类都有对应的监听器类. 如ItemEvent类对应的监听器接口ItemListener.监听器接口有:
ActionListener
AdjustmentListener
ComponenListener
ContainerListener
FocusListener
ItemListener
KeyListener
MouseListener
MouseMotionListener
MouseWheelListener
TextListener
WindowListener
WindowFocusListener
WindowStateListener

有的监听器接口中包括多个方法. 为了实现该接口时少写点代码. 提供了一些缺省适配类.
这些缺省适配类为接口中的每个方法提供了默认操作:
ComponentAdapter
ContainerAdapter
FocusAdapter
KeyAdapter
MouseAdapter
MouseMotionAdapter
WindowAdapter

 

8.3 AWT的语义事件和低层次事件

语义事件是明确表达用户动作的事件.如按下按钮.
低层事件是一些原始的事件.如按下按钮过程中可能会有鼠标左键按下.弹起等.


java.awt.event包中有:

4个语义事件类
ActionEvent 对应按钮按下. 菜单选择. 列表项选择 或者文本域输入回车.
AdjustmentEvent 对应用户调整了滚动条.
ItemEvent 对应用户从一组选择框或列表项中选择
TextEvent 对应文本域或文本框中的内容发生改变.

6个低层事件类
ComponentEvent 组件被缩放/移动/显示/隐藏 这个类是所有低层次事件类的基类.
KeyEvent 一个键被按下/释放
MouseEvent 鼠标键被按下/释放 鼠标移动/拖动
MouseWheelEvent 鼠标轮被滚动
FocusEvent 组件得到焦点/失去焦点
WindowEvent 窗口被激活/减活/图标化/还原/关闭
ContainerEvent 添加/删除一个组件


8.4 低层事件类型

8.4.1 键盘事件
例如用户同时按下SHIFT和A. 则生成5个事件:
1.按SHIFT事件  2.按A事件  3."输入A"事件  4.释放A事件  5.释放SHIFT事件
监听这些键盘事件需要KeyListener接口. 该接口的方法如下:
void keyPressed(KeyEvent e) //按下
void keyReleased(KeyEvent e) //释放
void keyTyped(KeyEvent e) //键入字符
可以用参数e的 getKeyChar() 或 getKeyCode()来得到键的字符或相关整数(虚拟键代码).

8.4.2 鼠标事件
如果是处理象点击按钮或选择菜单等.则不需要鼠标事件这样的低层事件.
而对于画图等则需要处理这类事件.

鼠标可以产生的事件包括: 按下/释放 单击 进入/离开 移动 拖动 鼠标滚轮旋转 .
有两种事件监听器接口来监听处理MouseEvent.
其中:
接口MouseListener处理: 按下/释放 单击 进入/离开. 它的适配器类为MouseAdapter.
    void mouseClicked(MouseEvent e) 单击键时
    void mouseEntered(MouseEvent e) 进入组件时
    void mouseExited(MouseEvent e) 离开组件时
    void mousePressed(MouseEvent e) 按下键时
    void mouseReleased(MouseEvent e) 释放键时
接口MouseMotionListener处理: 移动 拖动. 它的适配器类为MouseMotionAdapter.
    void mouseDragged(MouseEvent e) 在组件上按下并拖动时
    void mouseMoved(MouseEvent e) 移动到组件上但无按键按下时
使用两个监听器接口是为了效率. 因为大多时候我们并不想监听鼠标移动事件.
鼠标事件用MouseEvent类表示(除了鼠标滚轮事件).

MouseEvent的方法有:
    int getButton() 返回事件发生在哪个键上. 返回NOBUTTON BUTTON1 BUTTON2 BUTTON3之一.
    int getClickCount() 返回与此事件关联的鼠标单击次数。
    Point getPoint()  返回事件相对于源组件的 x、y 位置。
    int getX() 返回事件相对于源组件的水平 x 坐标。
    int getY() 返回事件相对于源组件的垂直 y 坐标。 
    String paramString() 返回标识此事件的参数字符串。 调试用.
    void translatePoint(int x, int y) 将事件的坐标平移到新位置

另外. MouseEvent继承自 InputEvent类. 所以它继承的方法还有下边的:
    public boolean isShiftDown()
    public boolean isControlDown()
    public boolean isMetaDown()
    public boolean isAltDown()
    public boolean isAltGraphDown()
    public long getWhen()
    public int getModifiers()
    public int getModifiersEx()
使用这些方法可以知道事件发生时每个鼠标键是否按下.可以知道Shift/Ctrl/Alt等键是否按下.

最后我们还可以更改鼠标光标. 要更改鼠标光标先要加载它. 使用java.awt.Toolkit类的方法:
public Cursor createCustomCursor(Image image, Point hotSpot, String name)
     其中参数: image是光标显示时的图像.
             hotSpot是光标的坐标(例如可以指定为图像的顶点或图像的中心).
             name是光标的说明.
加载光标后用java.awt.Component类的方法:
public void setCursor(Cursor cursor)
来设置光标.


8.4.3 焦点事件

例如文本框得到焦点时就会出现闪烁光标. 而按钮则在按钮标签周围显示矩形等.
同一个窗口中只能有一个组件具有焦点. 组件要得到焦点. 可能是用户点击了它. 也可能是使用Tab键切换的.
在组件得到或失去焦点时会产生FocusEvent事件. 要处理这些事件用监听器:
FocusListener接口(缺省适配器类是FocusAdapter)
它的方法有:
    void focusGained(FocusEvent e)  获得键盘焦点
    void focusLost(FocusEvent e)  失去键盘焦点

Component关于焦点的方法有:
    void requestFocus()  请求把焦点转移到该组件上
    void setFocusable(boolean )  设置是否允许组件获得焦点.
    boolean isFocusOwnwer()  检查组件是否已经获得焦点.

 


8.7 事件队列(core java 第6版)

当操作系统响应用户动作(如鼠标点击)时会生成操作系统事件. AWT把它转换为一个AWTEvent对象. 并添加到
事件队列中. 最后把AWTEvent从事件队列提取出来分派到合适的监听器上.调用监听器上对应的方法.

事件的处理可能需要较长的时间.所以产生的事件不可能立刻就被处理.所以要借助事件队列来缓冲一下.
我们也可以直接操作事件队列. 例如向事件队列中添加/删除事件对象. 这使用下边的函数如:
EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventqQueue(); //取事件队列
queue.postEvent(new ActionEvent(...)); //构造了一个事件并将其添加到事件队列
提取出下一个事件和查看下一个事件使用EventQueue类的方法:
AWTEvent peekEvent(); //查看下一个事件对象 (但不从队列删除)
AWTEvent getNextEvent(); //取出下一个事件对象(并从队列删除)


定制自己的事件类型

本小节我们将定制自己的时间事件并处理它. 定制事件有三个要素:
事件类.
事件监听器接口.
事件源.

首先定义一个自定义事件类TimerEvent. 因为AWT事件队列中的事件必须是AWTEvent类型的. 所以我们这个
TimerEvent要从AWTEvent派生:
class TimerEvent extends AWTEvent {
    public TimerEvent(Timer t) { super(t,TIMER_EVENT); }  //调用基类的构造方法
       //AWTEvent(Object source, int id)用指定的事件源和事件类型构造一个 AWTEvent 对象
    public static final int TIMER_EVENT = AWTEvent.RESERVED_ID_MAX+5555;
}

其中构造方法参数中的Timer是我们自己的事件源类. 下边我们定义这个类. 因为AWT事件机制要求事件源要
扩展自Component类
. 所以我们的事件源Timer类如下:

class Timer extends JComponent implements Runnable {
    private int interval;   //定时器事件的间隔时间
    private EventListenerList listeners;  //用来管理注册的监听器
    ///////////////////////////////////////////////////////////
    // EventListenerList 类
    // 用来管理注册的事件监听器对象. 它的方法包括(下边会用到这些方法):
    //     void  add(Class<T> t, T l)  //将监听器作为指定的类型添加
    //     void  remove(Class<T> t, T l) //将监听器作为指定的类型移除
    //     T[] getListeners(Class<T> t) //返回给定类型的所有侦听器组成的数组
    //////////////////////////////////////////////////////////////////////////

    public Timer(int i) {  //事件源的构造方法.
                           //它启动了一个线程. 该线程每隔interval时间会向事件队列加入一个
                           //TimerEvent对象.
        interval = i;
        Thread t = new Thread(this);
        t.start();
    }
    public void run() {
        while (true) {
            try { Thread.sleep(interval); }
            catch(InterruptedException e) {]
            EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
            TimerEvent event = new TimerEvent(this); //构造自定义事件并将它放入事件队列
            queue.postEvent(event);
        }
    }
    //此时我们已经让TimerEvent事件源源不断的插入AWT事件队列. 不过事情并没有结束. 因为我们
    //还要对这些事件进行处理呀. 处理事件是监听器类做的事. 但我们的事件源类要有添加/删除监听器
    //的方法. 管理这些监听器对象需要使用 EventListenerList类
. 如下:
    public void addTimerListener(TimerListener listener) {
        listenerList.add(TimerListener.class, listener);
    }
    public coid removeTimerListener(TimerListener listener) {
        listenerList.romve(TimerListener.class, listener);
    }
    //当从事件队列中移除事件对象.也就是要处理该事件对象时. 会调用事件源的processEvent方法.
    //所以我们的事件源要实现processEvent方法.
这里我们从listenerList中得到TimerListener
    //监听器对象的数组. 并调用每个监听器对象的timeElapsed方法.
    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);
    }
}

//我们要向事件源注册监听器. 下边定义我们的监听器接口
interface TimerListener extends EventLister {
    public void timeElapsed(TimerEvent event); //该接口中只声明一个方法
}

这样自定义事件需要的三个类都准备好了. 要使用上边的定时器事件. 可以如下:
Timer timer = new Timer(200);
timer.addTimerListener( new TimerListener() {
       public void timeElapsed(TimerEvent event) {
            //...
       }
    });