不灵敏的图形用户界面会降低应用程序的可用性。当以下现象出现的时候,我们通常说这个用户界面反应不灵敏:
不响应事件的现象;
没有更新的现象;
这些现象在很大程度上与事件的处理方法相关,而在编写Swing应用程序的时候,我们几乎必然要编写方法去响应鼠标点击按钮,键盘回车等事件。在这些方法中我们要编写一些代码,在运行时去触发一些动作。常见动作包括查找,更新数据库等。在这篇文章中通过对一个实例的分析,介绍了一些基本概念,常见的错误以及提出了一个解决方案。
event-dispatching thread
我们一定要记住,事件响应方法的代码都是在event-dispatching thread中执行的,除非你启用另一个线程。
那么,什么是event-dispatching thread呢?单一线程规则:一旦一个Swing组件被实现(realized),所有的有可能影响或依赖于这个组件的状态的代码都应该在event-dispatching thread中被执行。而实现一个组件有两种方式:
对顶层组件调用show(), pack(), 或者setVisible(true);
将一个组件加到一个已经被实现的容器中。
单一线程规则的根源是由于Swing组件库的大部分方法是对多线程不安全的。
为了支持单一线程模型,Swing组件库提供了一个专门来完成这些与Swing组件相关的操作的线程,而这一线程就是event-dispatching thread。我们的事件响应方法通常都是由这一线程调用的,除非你自己编写代码来调用这些事件响应方法。在这里初学者经常犯的一个错误就是在事件响应方法中完成过多的与修改组件没有直接联系的代码。其最有可能的效果就是导致组件反应缓慢。比如以下响应按钮事件的代码:
String str = null; this.textArea.setText("Please wait..."); try { //do something that is really time consuming str = "Hello, world!"; Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } this.textArea.setText(str); |
执行之后的效果就是按钮似乎定住了一段时间,直到Done.出现之后才弹起来。原因就是Swing组件的更新和事件的响应都是在event-dispatching thread中完成的,而事件响应的时候,event-dispatching thread被事件响应方法占据,所以组件不会被更新。而直到事件响应方法退出时才有可能去更新Swing组件。
为了解决这个问题,有人也许会试图通过调用repaint()方法来更新组件:
final String[] str = new String[1]; this.jTextArea1.setText("Please wait..."); this.repaint();
try { Thread.sleep(1000L); }catch(InterruptedException e) { e.printStackTrace(); } str[0] = "Done.";
jTextArea1.setText(str[0]); |
但是这一个方法没有起到预期的作用,按钮仍然定住一段时间,在察看了repaint()方法的源代码之后就知道原因了。
PaintEvent e = new PaintEvent(this, PaintEvent.UPDATE, new Rectangle(x, y, width, height)); Toolkit.getEventQueue().postEvent(e); |
repaint()方法实际上是在事件队列里加了一个UPDATE的事件,而没有直接去重画组件,而且这一个事件只能等待当前的事件响应方法结束之后才能被分配。因此只有绕过分配机制直接调用paint方法才能达到目的。
final String[] str = new String[1]; this.jTextArea1.setText("Please wait..."); this.paint(this.getGraphics());
try { Thread.sleep(1000L); }catch(InterruptedException e) { e.printStackTrace(); } str[0] = "Done.";
jTextArea1.setText(str[0]); |
这样却是实现了更新,但是还存在着以下的问题。虽然从感觉上,按钮已经弹起来了,但是在Done.出现之前,我们却无法按下这个按钮。可以说按钮还是定住了,只不过定在了弹起的状态。调用重绘方法无法从根本上解决问题,因此我们需要寻求其他的方法。
使用多线程
有效的解决方法是使用多线程。首先看一看一个更好的解决方案,这一方案是在参考《Rethinking Swing Threading》的一个程序片段完成的:
final String[] str = new String[1]; this.jTextArea1.setText("Please wait..."); this.repaint();
new Thread() { public void run() { try { Thread.sleep(1000L); }catch(InterruptedException e) { e.printStackTrace(); } str[0] = "Done."; javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { jTextArea1.setText(str[0]); } }); } }.start(); |
在这个程序中,要花费大量时间的操作被放到另一个线程当中,从而使事件响应方法能快速返回,event-dispatching thread就可以更新UI和响应其它事件了。注意到这个程序使用了invokeLater()方法。invokeLater()方法的作用是让event-dispatching thread去运行制定的代码。当然也可以不使用invokeLater()方法,但是这样就违背了单一线程原则,同时带来了一定程度的相对多线程的不安全性。到现在,解决方案似乎是完美的了,但是我们看一看在原来的程序添加下面的代码,尽管我们通常不这样做。
public void paint(java.awt.Graphics g) { super.paint(g); g.drawRect(1, 1, 100, 100); } |
我们会发现以前画的矩形被覆盖了一部分,原因是由于我们没用重画这一个矩形,因此在结尾加上对repaint()方法的调用。
final String[] str = new String[1]; this.jTextArea1.setText("Please wait..."); this.repaint();
new Thread() { public void run() { try { Thread.sleep(1000L); }catch(InterruptedException e) { e.printStackTrace(); } str[0] = "Done."; javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { jTextArea1.setText(str[0]); repaint(); } }); } }.start(); |
如果你认为这段代码过于缺乏可读性,可以通过SwingWorker来简化编程的方法。可以通过实现一个construct()方法来实现花费大量时间的操作和重写finished()方法来完成组件更新的工作。
this.jTextArea1.setText("Please wait...");
final SwingWorker worker = new SwingWorker() { public Object construct() { try { Thread.sleep(1000L); }catch(InterruptedException e) { e.printStackTrace(); } return "Done."; } public void finished() { jTextArea1.setText(getValue().toString()); repaint(); } }; worker.start(); |
以上的编程方式可以称为同步方式。另外作者提出了一个通过消息机制来实现相同功能的更清晰,但是需要编写更多代码的"异步"的方法。
结论 总之,我们在编写使用Swing组件的程序是要记住以下几点:
1、不要过多地占用event-dispatching thread;
2、与更新组件相关的代码要使用event-dispatching thread去执行;
3、要更新组件。
编写反应灵敏的图形用户界面还需要考虑很多问题,以上只是最基本的一部分。欢迎有兴趣的读者来信进行讨论。
分享到:
相关推荐
Java Swing 是Java编程语言中的一个图形用户界面(GUI)工具包,它是Java Foundation Classes (JFC)的一部分。在Java Swing中,你可以创建丰富的桌面应用程序,包括我们熟悉的计算器界面。本压缩包"Java swing编写的...
- **图形用户界面库**:Swing - **网络通信库**:Java Socket API - **数据流处理**:`DataInputStream` 和 `DataOutputStream` #### 三、系统架构 该聊天室系统主要由两部分组成:客户端(Client)与服务器(Server)...
下面将详细探讨这个项目所涉及的Java编程知识、图形用户界面设计以及文件管理。 首先,Java编程是整个项目的基础。Java是一种跨平台的编程语言,具有“一次编写,到处运行”的特性。在这个模拟QQ项目中,开发者可能...
### 图形用户界面设计 ...通过以上介绍,我们可以看到Java图形用户界面设计的强大功能以及Swing组件带来的诸多便利。掌握这些基本概念和技术后,开发者就可以构建出功能丰富且用户友好的应用程序。
在本实验中,我们主要探讨了Java中的图形用户界面(GUI)开发,特别是使用Java Swing库来构建交互式的应用程序。实验旨在让学生掌握以下几个关键知识点: 1. **JFrame的使用**: JFrame是Java Swing中用于创建窗口...
在Java中,图形用户界面主要由Java AWT(Abstract Window Toolkit)和Swing库提供支持。AWT是Java早期提供的GUI库,包含了一些基础的组件和事件模型。而Swing则是建立在AWT之上,提供了更为丰富的组件和更友好的外观...
《JAVA图形用户界面设计与实例》是陈家兴等作者在2004年出版的一本关于Java GUI设计的书籍。该书很可能涉及到使用Java编程语言开发图形用户界面(Graphical User Interface,简称GUI)的知识点,以及通过实例来加深...
jdbc连接数据库和Java swing图形化界面完成一个商品管理系统.zip”的压缩包中,包含了使用Java编程语言,结合JDBC(Java Database Connectivity)接口与MySQL数据库,以及利用Java Swing构建图形用户界面(GUI)来...
1. **Java Swing**:Java Swing是Java AWT(Abstract Window Toolkit)的扩展,提供了一套丰富的组件库,用于创建具有图形用户界面(GUI)的应用程序。在这个聊天室项目中,Swing被用来构建聊天窗口、输入框、发送...
总的来说,Java Swing 提供了一个强大而灵活的框架,使得开发者能够创建具有丰富交互性的图形用户界面,同时保持了Java的“一次编写,到处运行”的跨平台特性。通过深入学习和熟练掌握Swing,开发者可以构建出与操作...
### C++、C#、Java、Python 实现简单图形用户界面(GUI)的区别与方法 #### C++ 在C++中创建图形用户界面(GUI),主要依赖于Microsoft Foundation Classes(MFC)。MFC作为一套用于Windows应用程序开发的类库,...
在Java的图形用户界面中,AWT和Swing是两个重要的库。AWT(Abstract Window Toolkit)是Java早期的GUI库,而Swing是在Java 1.2中引入的,提供了更多的功能和更好的可定制性。尽管Swing比AWT更现代,但两者在许多项目...
在Java中,图形用户界面主要由AWT(Abstract Window Toolkit)和Swing库提供支持。 1. **AWT和Swing组件**: AWT是Java早期的GUI库,提供了基础的组件,如按钮、文本框、标签等。Swing是建立在AWT之上,提供了一套...
Java Swing是Java平台上用于构建图形用户界面(GUI)的一个强大工具包,它是Java Foundation Classes (JFC)的一部分。Swing提供了丰富的组件集,允许开发者创建功能完备、交互性强的桌面应用。本篇将深入探讨Java ...
“JavaSwing”标签再次强调了项目使用Swing库来创建用户界面,这对于理解项目的代码结构和功能至关重要。 在压缩包子文件的文件列表中,`.classpath`和`.project`是Eclipse项目的配置文件,它们包含了关于项目构建...
Java图形用户界面(GUI,Graphical User Interface)是Java编程中的一个重要组成部分,它允许开发者创建交互式的、视觉吸引人的应用程序。在Java中,GUI主要通过Java的AWT(Abstract Window Toolkit)和Swing库来...
Java 图形用户界面设计实验是 Java programming 语言中的一项重要实验,旨在让学生掌握 Java Swing 组件的使用方法,理解委托事件处理模型,掌握多种布局方式,掌握窗口菜单和快捷菜单设计方式。通过本实验,学生将...
### Java图形用户界面设计 #### 一、图形用户界面(GUI)概述 在现代软件开发领域,图形用户界面(Graphical User Interface, GUI)是人机交互的重要方式之一。通过直观的图形界面,用户可以轻松地操作软件应用。...
在Java编程领域,Swing库是用于创建图形用户界面(GUI)的重要工具。"Swing编写的日历和时钟"这个主题涉及到如何利用Swing组件来构建一个实用的日历和时钟应用。下面我们将详细探讨相关的知识点。 1. **Swing基础**...
通过这些章节的学习,读者可以逐步掌握如何利用Java Swing构建一个完整的、具有交互性的图形用户界面。同时,书中提供的源代码实例有助于读者将理论知识应用于实践中,加深理解并提升编程技能。对于Java初学者而言,...