快精灵印艺坊 您身边的文印专家
广州名片 深圳名片 会员卡 贵宾卡 印刷 设计教程
产品展示 在线订购 会员中心 产品模板 设计指南 在线编辑
 首页 名片设计   CorelDRAW   Illustrator   AuotoCAD   Painter   其他软件   Photoshop   Fireworks   Flash  

 » 彩色名片
 » PVC卡
 » 彩色磁性卡
 » 彩页/画册
 » 个性印务
 » 彩色不干胶
 » 明信片
   » 明信片
   » 彩色书签
   » 门挂
 » 其他产品与服务
   » 创业锦囊
   » 办公用品
     » 信封、信纸
     » 便签纸、斜面纸砖
     » 无碳复印纸
   » 海报
   » 大篇幅印刷
     » KT板
     » 海报
     » 横幅

Swing之EventQueue简介


    在Swing的GUI程序中,EventQueue是一个重要的部分,它负责所有AWTEvent(以及其子类)的分发

    EventQueue简朴工作原理

    简朴来讲,在EventQueue中有一个dispatchThread,这是一个线程类,负责事件的分发,当Queue中有事件的时候,它会摘取前面的事件并分发给相应的对象进行处理,等处理完之后再获取下一个,当Queue中没有事件的时候,线程等待。

    当有事件触发时,系统会调用EventQueue的push方式将AWTEvent添加到EventQueue的最后,同时唤醒dispatchThread。 

    为什么界面会死掉

    所以可以看到,Swing的事件分发实际上是同步的,并且都是在dispatchThread这个线程中处理的,也就是说是一个事件一个事件处理的,假如有某一个事件处理的时间异常长的时侯,其他事件就会被堵塞在那里,从现象上看得话,就是界面会死掉,假如界面被其他窗口覆盖之后再回到前面的时侯,会变成一片灰色,这是因为PaintEvent被堵塞而不能被分发出去的缘故。

    为什么Modal Dialog(Frame)弹出的时候界面不会死

    当在处理事件的时侯假如弹出一个Modal Dialog,那么处理方式会停在那里并等待Modal Dialog销毁,这个时候按照上面的分析,dispatchThread也会停在那里,这样的话其他事件也不会被分发,那么界面也应该会死掉才对。实际上在等待Modal Dialog销毁的过程中,假如能够保证事件可以顺利地分发出去的话,界面当然就不会死。先来看这个例子。

import javax.swing.JDialog;import javax.swing.JButton;import java.awt.event.ActionListener;import java.awt.event.ActionEvent;import java.awt.EventQueue;import java.awt.AWTEvent;import java.awt.ActiveEvent;import java.awt.event.PaintEvent;import java.awt.Component;import java.awt.MenuComponent;
public class TestEvent {    public static void main(String[] args) {        final JDialog dlg = new JDialog();        dlg.setTitle("Test Event Queue");        JButton btn = new JButton("Test");        dlg.getContentPane().add(btn);        btn.addActionListener(new ActionListener(){            public void actionPerformed(ActionEvent e){                long now = System.currentTimeMillis();                EventQueue theQueue = dlg.getToolkit().getSystemEventQueue();                System.out.println("at least 5000 millis");                while (System.currentTimeMillis() - now < 5000l) {                    try {                        // This is essentially the body of EventDispatchThread                        AWTEvent event = theQueue.getNextEvent();                        Object src = event.getSource();                        if (event instanceof ActiveEvent) {                            ((ActiveEvent) event).dispatch();                        }                        else if (src instanceof Component) {                           ((Component) src).dispatchEvent(event);                        }                        else if (src instanceof MenuComponent) {                            ((MenuComponent) src).dispatchEvent(event);                        }                    }                    catch (Exception ex) {                        ex.printStackTrace();                    }                }                System.out.println("end");            }        });        dlg.pack();        dlg.show();    }}

在上面的例子中,当Button的Action被触发,actionPerformed方式执行的时候,会首先帮助EventQueue分发事件,直到最少5秒之后返回,这时可以看到这个事件处理方式至少执行了5秒钟,但是在这个过程中Dialog仍旧可以正常工作,只是因为在这5秒之中并非是Sleep,而是在帮助EventQueue分发事件,假如代码改成    Thread.sleep(5000);的话,界面将会死掉。


public class TestEvent {    public static void main(String[] args) {        final JDialog dlg = new JDialog();        dlg.setTitle("Test Event Queue");        JButton btn = new JButton("Test");        dlg.getContentPane().add(btn);        btn.addActionListener(new ActionListener(){            public void actionPerformed(ActionEvent e){                long now = System.currentTimeMillis();                EventQueue theQueue = dlg.getToolkit().getSystemEventQueue();                System.out.println("at least 5000 millis");                while (System.currentTimeMillis() - now < 5000l) {                    try {                        // This is essentially the body of EventDispatchThread                        AWTEvent event = theQueue.getNextEvent();                        Object src = event.getSource();                        if (event instanceof ActiveEvent) {                            ((ActiveEvent) event).dispatch();                        }                        else if (src instanceof Component) {                           ((Component) src).dispatchEvent(event);                        }                        else if (src instanceof MenuComponent) {                            ((MenuComponent) src).dispatchEvent(event);                        }                    }                    catch (Exception ex) {                        ex.printStackTrace();                    }                }                System.out.println("end");            }        });        dlg.pack();        dlg.show();    }}

在上面的例子中,当Button的Action被触发,actionPerformed方式执行的时候,会首先帮助EventQueue分发事件,直到最少5秒之后返回,这时可以看到这个事件处理方式至少执行了5秒钟,但是在这个过程中Dialog仍旧可以正常工作,只是因为在这5秒之中并非是Sleep,而是在帮助EventQueue分发事件,假如代码改成    Thread.sleep(5000);的话,界面将会死掉。

    所以在Modal Dialog弹出的时候,实际上只要在show方式中能够实现类似上面的代码,保证事件可以正常的分发(同时截获父窗口的一些事件,过滤掉一些触发Action的事件),那么父窗口的界面就不会死掉。

    当事件处理方式很长时间才能做完该怎么办

    当事件处理方式需要很长时间才能执行完的话,假如需要保证界面不死的话,还是只能用多线程,虽然上面的方式实现了事件处理的时候界面不死,但是这和一般的事件处理是有不同的,上面的方式实际上在处理的时候什么都没有做,而我们一般需要有自己的操作(比如访问数据库,访问网络,读写操作等需要很长时间才能处理完的工作),不可能做到一边在操作一边处理Event分发,这个时候只有新建一个线程才是正道。

关于EventQueue的一些方式

    Window.getToolkit().getSystemEventQueue();    获取系统的EventQueue

    SwingUtilities.isEventDispatchThread();    当前线程是否为EventDispatchThread

    EventQueue.push(EventQueue queue);    将一个EventQueue作为当前EventQueue的nextQueue,实际上事件是由最后一个EventQueue来分发的

    EventQueue.getNextEvent();    获取下一个事件,假如没有,则等到有再返回

   EventQueue.postEvent(AWTEvent theEvent);   添加一个Event

  不过关于很多EventQueue和EventDispatchThread的方式都被封装在其实现里面,对外不可视,导致不可能对其进行一些修改,有点不爽。另外在EventQueue中的AWTEvent一般都是给最上层对象的,比如最上层的JDialog或者JFrame,然后由JDialog或者JFrame分发给其他的Component,不过我不知道怎么可以从AWTEvent事件找到真正的拥有者,这一点比较郁闷




返回类别: 教程
上一教程: 每个初学者都应该搞懂的问题!
下一教程: [JAVA]Panel 嵌入ScrollPane

您可以阅读与"Swing之EventQueue简介"相关的教程:
· 用Swing的Jtable类让数据看起来更干净
· java Excel API 简介
· Java Media简介
· java swing的拖放例子
· 数据库连接池简介
    微笑服务 优质保证 索取样品