`

Swing框架之Component:续文二

阅读更多

         Swing的事件处理过程为:事件调度线程(Event Dispatch Thread)从事件队列(EventQueue)中获取底层系统捕获的原生事件,如鼠标、键盘、焦点、PAINT事件等。接着调用该事件源组件的 dispachEvent。该方法过滤出特殊事件后,调用processEvent进行处理。processEvent方法根据事件类型调用注册在这个组 件上的相应事件处理器函数。事件处理器函数根据这些事件的特征,判断出用户的期望行为,然后根据期望行为改变组件的状态,然后根据需要刷新组件外观,触发 带有特定语义的高级事件。此事件继续传播下去,直至调用应用程序注册在该组件上的处理器函数。下图是这个过程的示意图:

 

         上图所示意的过程简要说就是:

Pump an Event->Dispatch & Process Event->MouseListener.mousePressed->fireActionPerformed->ActionListener.actionPeformed->Do database query and display result to a table->Return from actionPerformed->Return from fireActionPerformed->Return from MouseListener.mousePressed->Pump another Event.

         事件调度线程在应用程序事件处理函数actionPerformed没有完成之前是不能处理下一个事件的,如果应用程序处理函数是一个时间复杂的任务(比 如查询数据库并将结果显示到表格中),后面包括PAINT事件将在长时间内得不到执行。由于PAINT事件负责将界面更新,所以这就使用户界面失去响应。

          打一个比方,事件处理线程就像进入某城唯一的单行道一样,事件相当于汽车。有种PAINT汽车负责为城市运输非常重要的生活物资。但是有一天,PAINT 前面有一辆汽车突然坏掉了,司机下来修车。但是这车太难修,一修就是几天,结果后面的PAINT汽车无法前进,物资无法按时运到城里。市民急了,市长虽然 不停的打电话催PAINT公司,但即使PAINT公司多添加几辆车也没用。由于进城的唯一条路被那辆车给占着,所以再多的PAINT车也只能堵在路上。

         不了解Swing的这种事件处理模型的人往往将时间复杂的任务放在处理函数中完成,这是造成Swing应用程序速度很慢的原因。用户触发这个动作,用户界 面就失去了响应,于是给用户的感觉就是Swing太慢了。其实这个错误是程序员造成的,并不是Swing的过失。

         说点题外话,所有采用这种事件模型的用户界面工具都会产生这种问题,包括SWT、GTK、MFC等流行的用户界面工具。之所以只有Swing被误解,主要是和Swing的历史、市场时机、商业宣传策略和心理学相关的。

         首先Swing的历史和市场时机极差。Swing出现早期性能也差、错误也多,而Java程序员脱身于传统图形界面工具,对于Swing这种新的事件处理 模型并不太了解,而此时正处于Java第一轮狂热的时期,大家都满怀希望做了大量的Swing应用程序,而这些程序中大量存在这种错误方法。于是市场上涌 现了大批的这种程序。自从那个时代,因为这些程序,Swing被贴上了慢的标签。又由于当时的Swing界面也丑,和一般的Windows程序风格炯异, 更加深人们的这种印象。这种印象一直持续到现在,像烙印一样深深的刻在人们的脑海里。

           其次,Swing还有一个致命的问题,就是没有涌现出一个具有标识性的好程序,这是造成它比SWT印象惨的原因。为什么SWT采用相同的事件处理模型,而 获得了速度快的声誉呢?这是因为人们当时对于Java做桌面应用的期望心理达到了低谷,而SWT的出现恰恰是伴随Eclipse出现的,早期的 Eclipse的确是在速度快、界面漂亮,这一扫当时人们认为Java慢,Java界面丑陋,Java无法做桌面应用的印象,继而这个印象被加在SWT身 上,人们认为Eclipse速度快、漂亮是因为SWT,其实如果你知道Swing/SWT事件处理模型的话,你就明白功劳是Eclipse开发者的, Eclipse界面漂亮其实要归功于Eclipse界面设计专家,他们的高水平造就了这个好的IDE,从而也抬起了SWT的声誉。而Swing的名誉恰恰 就被早期Swing低水平开发者给毁了。

        再次, 这和商业宣传策略有关。IBM和Eclipse很懂得市场宣传,人们不是认为Java慢吗,就宣传SWT使用原生组件,人们不是认为Swing丑陋、风格 炯异吧,就宣传SWT风格一致性,人们不是认为Java不能做桌面应用吗,就宣传基于SWT的Eclipse。其实这一切的背后原因只是“人”的不同, Eclipse的开发者和Swing应用程序的开发者,Swing和SWT技术差异并没有造成那么大的差别,如果是相近能力的人使用他们开发的话,应该能 做出相近的产品。这可以从现在Eclipse和NetBeans、Intellij IDEA、JDeveloper和JBuilder看的出来。

         最后,人类有一个心理学现象,就是一旦形成对某种事物的印象,很难摆脱旧的认识,有时甚至人们不愿意承认摆在眼前的事实。总而言之,Swing和SWT不同遭遇是因为历史、市场时机、商业宣传策略、心理学的种种原因造成的。

          那么如何避免这个问题,编写响应速度快的Swing应用程序呢?在SwingWorker的javadoc中有这样两条原则:

Time-consuming tasks should not be run on the Event Dispatch Thread. Otherwise the application becomes unresponsive. 耗时任务不要放到事件调度线程上执行,否则程序就会失去响应。

Swing components should be accessed on the Event Dispatch Thread only. Swing组件只能在事件调度线程上访问。

         因此处理耗时任务时,首先要启动一个专门线程,将当前任务交给这个线程处理,而当前处理函数立即返回,继续处理后面未决的事件。这就像前面塞车的例子似的,那个司机只要简单的把车开到路边或者人行道上修理,整个公路系统就会恢复运转。

         其次,在为耗时任务启动的线程访问Swing组件时,要使用SwingUtilties. invokeLater或者SwingUtilities.invokeAndWait来访问,invokeLater和invokeAndWait的参 数都是一个Runnable对象,这个Runnable对象将被像普通事件处理函数一样在事件调度线程上执行。这两个函数的区别是, invokeLater不阻塞当前任务线程,invokeAndWait阻塞当前线程,直到Runnable对象被执行返回才继续。在前面塞车的例子中, 司机在路边修车解决了塞车问题,但是他突然想起来要家里办些事情,这时他就可以打个电话让家里开车来。假如修车不受这件事情的影响,比如叫家人送他朋友一 本书,他可以继续修车,这时就相当于invokeLater;假如修车受影响,比如缺少某个汽车零件,叫家人给他送过来,那么在家人来之前,他就没法继续 修车,这时就相当于invokeAndWait。

         下面举一个例子说明这两点,比如按下查询按钮,查询数据量很大的数据库,并显示在一个表中,这个过程需要给用户一个进度提示,并且能动态显示表格数据动态增加的过程。假设按钮的处理函数是myButton_actionPerformed,则:

void myButton_actionPerformed(ActionEvent evt){
     new MyQueryTask().start();
}
public class MyQueryTask extends Thread{
    public void run(){
        //查询数据库
        final ResultSet result=...;
       / /显示记录
      for(;result.next();){
          //往表的Model中添加一行数据,并更新进度条,注意这都是访问组件
         SwingUtilities.invokeLater(new Runnable(){
              public void run(){
                    addRecord(result);
              }
         });
      }
   ....
   }
   void addRecord(ResultSet result){
       //往表格中添加数据
       jTable.add....
      //更新进度条
      jProgress.setValue(....);
    }
}

         JDK1.6以后,Swing提供了一个专门的类SwingWorker能帮你解决这个编程范式,你所需要做的就是继承这个类,重载 doInBackground,然后在actionPeformed中调用它的execute方法,并通过publish/process方法来更新界 面。SwingWorker的主要方法和它们的作用在下面的示意图:

          从上面示意图可以看出,SwingWorker实际上不过是封装了前面我所说的例子中的MyQueryTask,并做了更详尽的考虑。execute方法 相当于MyQueryTask线程start,它启动这个后台线程并立刻返回。SwingWorker可以注册 PropertyChangeListener,这些listener都被在事件调度线程上执行,相当于MyQueryTask中的那些访问组件的 Runnable对象。另外,publish、setProgress只不过是特殊的property事件吧,process和done不过是响应 publish和PropertyChangeEvent.DONE这个事件的方法罢了。因此我们很容易将上面的例子改成SwingWorker的版本:

void myButton_actionPerformed(ActionEvent evt){
    new MyQueryTask().execute();
}

public class MyQueryTask extends SwingWorker{
    public void doInBackground(){
        //查询数据库
        final ResultSet result=...;
        //显示记录
        for(;result.next();){
            //往表的Model中添加一行数据,并更新进度条,注意这都是访问组件
            publish(result);
        }
        ....
    }
    public void process(Object ... result){
        //往表格中添加数据
        jTable.add....
        //更新进度条
        jProgress.setValue(....);
    }
}

         对于一般的耗时任务这样做是比较普遍的,但是有一些任务是一旦触发之后,会周期性的触发,如何做处理这种任务呢?JDK中提供了两个Timer类帮你完成 定时任务,一个是javax.swing.Timer,一个java.util.Timer。使用它们的方法很简单,对于Swing的timer,使用方 法如下:

public void myActionPerformed(){
    //假设点击了某个按钮开始记时
    Action myAction=new AbstractAction(){
        public void actionPerformed(ActionEvent e){
            //做周期性的活动,比如显示当前时间
            Date date=new Date();
            jMyDate.setDate(date);//jMyDate是个假想的组件,能显示日期时间
        }
    };
    new Timer(1000, myAction).start();
}

         java.util.Timer类似,只不过使用TimerTask完成动作封装。注意这两个Timer有一个关键的区别:Swing的Timer的事件 处理都是在事件调度线程上进行的,因而它里面的操作可以直接访问Swing组件。而java.util.Timer则可能在其他线程上,因而访问组件时要 使用SwingUtilities.invokeLater和invokeAndWait来进行。这一点要记住。

         如果要了解更详细的信息,可以查阅SwingWorker、Swing Timer和util Timer这些类javadoc文档和其他网上资料。最重要的是要记住了那两条原则。

分享到:
评论

相关推荐

    Swing框架定制Component

    ### Swing框架定制Component详解 Swing作为Java图形用户界面(GUI)编程的重要组成部分,提供了丰富的组件库和灵活的自定义选项。本文旨在深入探讨Swing框架中Component的定制方法,特别是GUI的绘制机制以及Swing的...

    精通java-swing开发.doc

    `JComponent`是Swing组件层次结构的基础,它扩展了AWT的`Component`类,添加了许多高级特性,如内置的事件处理、可定制的外观(LookAndFeel)以及丰富的组件属性。通过`JComponent`,开发者可以方便地自定义组件的...

    Java Swing大神必经之路

    第2篇 Java Swing大神必经之路2:啃透JDK源码-JCheckBox详解 本篇文章详细介绍了 JCheckBox 的继承体系和常用方法,包括构造方法、isSelected()、setText() 等,幫助讀者更好地理解 JCheckBox 的使用。 第3篇 Java ...

    swing教程之经典

    - **示例2:使用Swing创建复杂的应用程序**:演示如何利用Swing的丰富功能,如表格、列表、树状视图等,来构建功能强大的应用程序。 #### 六、学习资源推荐 - **官方文档**: Java SE官方文档提供了Swing的详细指南...

    swing框架详细讲解

    Swing框架是Java GUI开发中的一个重要部分,它是建立在AWT(Abstract Window Toolkit)之上,提供了更丰富的组件和更现代的用户界面。Swing使用Java2D API来绘制组件,确保了平台独立性,使得在不同操作系统上都能...

    Java_Swing_with_Samples.pdf

    Java Swing是Java平台的一个图形用户界面(GUI)工具包,它构建于AWT之上,并提供了一系列更丰富、更强大的组件来帮助开发者创建美观且功能丰富的应用程序。Swing不仅支持基本的GUI组件如按钮、文本框等,还提供了更...

    Java Swing桌面开发经典案例

    2. **组件体系结构**: Swing组件基于JComponent类,它是AWT Component类的子类。JComponent类提供了事件处理、定制外观和行为的能力。例如,JButton、JLabel、JFrame、JPanel等都是JComponent的子类。 3. **布局...

    Swing用户界面设计

    Swing用户界面设计是Java...总的来说,Swing用户界面设计提供了一个强大而灵活的框架,用于构建功能丰富的Java应用程序。通过深入理解和熟练掌握Swing,开发者可以创建出符合现代用户需求的、具有吸引力的图形界面。

    计算机软件及应用jaaPPT课件.pptx

    Swing组件的继承层次结构始于`java.lang.Object`,然后是`java.awt.Component`,接着是`java.awt.Container`,再是`javax.swing.JComponent`。从`JComponent`派生出的常用组件包括`JLabel`, `JTextField`, `...

    swing圆角矩形按钮

    根据给定的信息,本文将详细解释如何在Java Swing框架中创建具有圆角效果的自定义按钮。这将涉及Swing的基本概念、自定义组件的方法以及如何实现特定的视觉效果。 ### Swing简介 Swing是Java平台的一个图形用户...

    Swing之JTable详解

    在Swing框架中,`JTable`是构建GUI应用程序中表格的主要组件。它继承自`JComponent`类,这使得`JTable`能够支持丰富的图形用户界面功能。下面展示了`JTable`的类层次结构: 1. `java.lang.Object` - `java.awt....

    swing jframe 窗体抓取 (源码)

    例如,可以使用JUnit或TestNG等单元测试框架,结合Swing提供的`Robot`类来模拟键盘和鼠标操作,配合窗体抓取技术,实现对GUI应用的自动化测试。 在这个项目中,"AmsSwing"可能是项目的源代码文件夹,包含了实现窗体...

    java GUI 参考资料

    11. **Swing框架之Component**:"Swing框架之Component.txt"可能深入讨论了Component类,它是所有Swing组件的基类,包含了一些基本的属性和方法,如可见性、尺寸、位置等。 以上就是基于给定的文件名和描述所推断出...

    swing演示排序动画

    本项目通过Swing框架实现了一个动态演示排序过程的应用程序。主要目标是让用户能够直观地看到各种排序算法(例如冒泡排序、插入排序等)在数组上的执行过程。 #### 二、程序结构分析 ##### 1. `AlgorithmAnimation...

    cyber_kustom_component:该框架旨在帮助开发接口,为组件提供易于编辑的特征

    cyber_kustom_component 该项目旨在开发具有可定制的Swing组件的库。 使用Cyber​​KustomComponent库,您可以快速编辑组件的主要视觉特征。 要将新组件添加到netbeans面板,请。 可以通过下载Jar 版本0.1.0具有...

    Component.jar 自己封装的框架

    基于SWing,聊天软件的框架。 可以重用,基本修改,是改变了所有的透明特性。

    Swing换肤——JavaSE技术

    Swing则是在AWT之上构建的,它使用纯Java实现,因此提供了更好的跨平台一致性。Swing组件不仅比AWT更丰富,还支持自定义外观,这使得实现换肤成为可能。 Swing的外观(LookAndFeel)机制允许开发者通过更换不同的...

    JavaSwing用户界面设计[IT_CLUB].doc

    Swing组件的类层次结构主要建立在AWT的Component和Container类之上。JComponent是大部分Swing组件的基类,它扩展了AWT的Component,添加了更多的特性和功能。 5. Swing包: Swing包含多个子包,如javax.swing.*,...

    Java教程编程中国\\JAVA教程 第七讲 Swing用户界面设计

    创建一个Swing应用程序的基本步骤包括设置主类、初始化框架、添加组件以及设置布局管理器。 **5.2 使用Swing组件** Swing提供了丰富的组件库,如按钮、文本框、列表、树等。开发者可以通过调用组件类的方法来定制...

Global site tag (gtag.js) - Google Analytics