﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Swing-随笔分类-Swing</title><link>http://www.blogjava.net/Swing/category/22030.html</link><description>&lt;font size="3"&gt;&lt;br&gt;&lt;font color="#669933"&gt;天行健  君子以自强不息&lt;/font&gt;&lt;br&gt;&lt;br&gt;&lt;/font&gt;
</description><language>zh-cn</language><lastBuildDate>Tue, 01 Apr 2008 05:51:03 GMT</lastBuildDate><pubDate>Tue, 01 Apr 2008 05:51:03 GMT</pubDate><ttl>60</ttl><item><title>TWaver 最新2.0版 发布，附在线运行demo</title><link>http://www.blogjava.net/Swing/archive/2008/03/27/188989.html</link><dc:creator>zht</dc:creator><author>zht</author><pubDate>Thu, 27 Mar 2008 05:56:00 GMT</pubDate><guid>http://www.blogjava.net/Swing/archive/2008/03/27/188989.html</guid><wfw:comment>http://www.blogjava.net/Swing/comments/188989.html</wfw:comment><comments>http://www.blogjava.net/Swing/archive/2008/03/27/188989.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.blogjava.net/Swing/comments/commentRss/188989.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Swing/services/trackbacks/188989.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: TWaver是一款优秀的用户图形界面组件软件，主要用于电信网管软件或其他行业的网络管理软件的图形界面开发。TWaver是可以让开发者快速开发出高效、灵活、高质量的软件的产品。<br>&nbsp;&nbsp;<a href='http://www.blogjava.net/Swing/archive/2008/03/27/188989.html'>阅读全文</a><img src ="http://www.blogjava.net/Swing/aggbug/188989.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Swing/" target="_blank">zht</a> 2008-03-27 13:56 <a href="http://www.blogjava.net/Swing/archive/2008/03/27/188989.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TWaver简介——高效的OSS SwingGUI</title><link>http://www.blogjava.net/Swing/archive/2008/03/09/184822.html</link><dc:creator>zht</dc:creator><author>zht</author><pubDate>Sun, 09 Mar 2008 05:04:00 GMT</pubDate><guid>http://www.blogjava.net/Swing/archive/2008/03/09/184822.html</guid><wfw:comment>http://www.blogjava.net/Swing/comments/184822.html</wfw:comment><comments>http://www.blogjava.net/Swing/archive/2008/03/09/184822.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/Swing/comments/commentRss/184822.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Swing/services/trackbacks/184822.html</trackback:ping><description><![CDATA[<span style="color: #008000"><span style="font-size: 12pt"><span style="color: #008000"><span style="font-size: 10pt"><span style="color: #008000"><span><span style="color: #008000">最近两天研究了一下TWaver，以下是关于TWaver的一些信息，均摘自TWaver网站和使用手册。<br />
<br />
TWaver是一套基于Java技术的界面软件开发组件包，主要应用于电信行业，也可应用于电力、金融、制造、交通等行业的软件开发中。一般的Java软件系统图形界面开发需要花费大量的时间和人力，运行效率低下，界面不够专业美观。如果采用TWaver组件来开发Java软件界面，可以非常简单快速的创建精美、专业、高效的图形化界面，提高开发和运行效率，增加客户满意度，提高软件竞争力。<br />
TWaver由以下部分组成：
<li>数据容器组件；
<li>预定义业务对象集合；
<li>Swing组件集合；
<li>数据编辑器；
<li>XML数据管理模块；
<li>Web扩展；
<li>服务器集成框架；
<p>&nbsp;
<div align="center"><img height="421" alt="" src="http://www.blogjava.net/images/blogjava_net/swing/TWaver/TWaverStr.PNG" width="587" border="0" /></div>
<p>&nbsp;</p>
TWaver的优点<br />
1、纯Java组件：TWaver 100%基于Java和Swing技术，适用于所有支持Java的平台。<br />
2、MVC框架：TWaver提供了设计很好的MVC框架结构，开发人员可以很容易地使用和扩展。<br />
3、高性能：TWaver支持大量数据的显示，可以很容易地处理成百上千的节点。<br />
4、XML支持：支持XML数据源，通过xml，可以很容易的从其他平台或者后台传递数据到本系统显示。<br />
5、支持可视化编辑：提供network/设备组件数据的可视化编辑，可以使用户很容易的编写网络拓扑和设备模板。<br />
<br />
<br />
以下为一些TWaver组件截图<br />
Network:<br />
<div align="center"><img height="466" alt="" src="http://www.blogjava.net/images/blogjava_net/swing/TWaver/network.PNG" width="708" border="0" /></div>
<br />
<span lang="EN-US" style="font-size: 10pt; font-family: Arial; mso-fareast-font-family: 'Arial Unicode MS'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-bidi-font-family: 'Times New Roman'">Equipment Chassis:<br />
<div align="center"><img height="501" alt="" src="http://www.blogjava.net/images/blogjava_net/swing/TWaver/equ.PNG" width="384" border="0" /><br />
</div>
<br />
Tree&amp;Table<br />
<div align="center"><img height="423" alt="" src="http://www.blogjava.net/images/blogjava_net/swing/TWaver/tree.PNG" width="335" border="0" /><br />
<br />
<img height="480" alt="" src="http://www.blogjava.net/images/blogjava_net/swing/TWaver/tree2.PNG" width="286" border="0" /><br />
</div>
</span>
<p><span lang="EN-US" style="font-size: 10pt; font-family: Arial; mso-fareast-font-family: 'Arial Unicode MS'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-bidi-font-family: 'Times New Roman'">
<div align="center"><img height="237" alt="" src="http://www.blogjava.net/images/blogjava_net/swing/TWaver/treetable.PNG" width="611" border="0" /><br />
<br />
<img height="401" alt="" src="http://www.blogjava.net/images/blogjava_net/swing/TWaver/alarm.PNG" width="459" border="0" /></div>
</span>
<p>&nbsp;</p>
<p><span lang="EN-US" style="font-size: 10pt; font-family: Arial; mso-fareast-font-family: 'Arial Unicode MS'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-bidi-font-family: 'Times New Roman'">List:</span></span></span></span></span></span></span></span></p>
<div align="center"><img height="276" alt="" src="http://www.blogjava.net/images/blogjava_net/swing/TWaver/list.PNG" width="198" border="0" /></div>
<br />
<span style="color: #008000">Chart:</span><br />
<div align="center"><img height="417" alt="" src="http://www.blogjava.net/images/blogjava_net/swing/TWaver/chartx.PNG" width="546" border="0" /></div>
<br />
<span style="color: red"><span style="color: #008000">通过TWaver即使不懂Swing的Java开发人员也可以很容易的开发出漂亮的Swing应用<br />
以上信息均摘录自赛瓦软件网站及软件使用指南。<br />
有关TWaver的详细信息，请访问<a style="font-size: 14pt" href="http://www.servasoftware.com/">http://www.servasoftware.com</a></span></span><span style="color: #008000"><br />
<br />
后续将推出一些关于TWaver如何使用的文章，敬请关注<img alt="" src="/CuteSoft_Client/CuteEditor/images/emsmiled.gif" align="absMiddle" border="0" /><br />
</span></li>
 <img src ="http://www.blogjava.net/Swing/aggbug/184822.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Swing/" target="_blank">zht</a> 2008-03-09 13:04 <a href="http://www.blogjava.net/Swing/archive/2008/03/09/184822.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Swing深入学习</title><link>http://www.blogjava.net/Swing/archive/2007/10/18/153792.html</link><dc:creator>zht</dc:creator><author>zht</author><pubDate>Thu, 18 Oct 2007 03:03:00 GMT</pubDate><guid>http://www.blogjava.net/Swing/archive/2007/10/18/153792.html</guid><wfw:comment>http://www.blogjava.net/Swing/comments/153792.html</wfw:comment><comments>http://www.blogjava.net/Swing/archive/2007/10/18/153792.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/Swing/comments/commentRss/153792.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Swing/services/trackbacks/153792.html</trackback:ping><description><![CDATA[<span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;做了很长时间的Swing了，感觉有很多知识还是一知半解，打算抽时间把Swing书再看一遍，加深一下理解，着重于Swing底层知识、JTable，JTree。<br />
<br />
</span>
<table cellspacing="2" cellpadding="2" width="500" border="0">
    <tbody>
        <tr>
            <td><span style="color: #008000"><span style="color: #993300"><span style="color: #ff6600">序号</span></span></span></td>
            <td><span style="color: #008000"><span style="color: #993300"><span style="color: #ff6600">目录</span></span></span></td>
            <td><font color="#ff6600"></font></td>
        </tr>
        <tr>
            <td><span style="color: #008000">1</span></td>
            <td><span style="color: #008000">Swing基础知识</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">2</span></td>
            <td style="color: red"><span style="color: #ff0000">Swing组件的体系结构</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">3</span></td>
            <td><span style="color: #ff0000">JComponent类</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">4</span></td>
            <td><span style="color: #008000">边框图标和动作</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">5</span></td>
            <td><span style="color: #008000">实用工具</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">6</span></td>
            <td><span style="color: #ff0000">插入式界面样式</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">7</span></td>
            <td>
            <p><span style="color: #008000">标签、按钮、反转按钮、<br />
            复选框、单选钮&nbsp;</span></p>
            </td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">8</span></td>
            <td><span style="color: #008000">菜单和工具条</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">9</span></td>
            <td><span style="color: #008000">进度条、滑杆和分割条</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">10</span></td>
            <td><span style="color: #008000">轻量容器</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">11</span></td>
            <td><span style="color: #ff0000">滚动</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">12</span></td>
            <td>
            <p><span style="color: #008000">窗口、对话框、内部窗格、</span></p>
            <p><span style="color: #008000">桌面窗格</span></p>
            </td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">13</span></td>
            <td><span style="color: #008000">选取器</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">14</span></td>
            <td><span style="color: #008000">列表、组合框</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">15</span></td>
            <td><span style="color: #ff0000">表格</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">16</span></td>
            <td><span style="color: #ff0000">树</span></td>
            <td></td>
        </tr>
        <tr>
            <td><span style="color: #008000">17</span></td>
            <td><span style="color: #008000">文本</span></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </tbody>
</table>
<div style="border-right: #666666 1px solid; border-top: #666666 1px solid; border-left: #666666 1px solid; border-bottom: #666666 1px solid">Type text here</div>
<img src ="http://www.blogjava.net/Swing/aggbug/153792.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Swing/" target="_blank">zht</a> 2007-10-18 11:03 <a href="http://www.blogjava.net/Swing/archive/2007/10/18/153792.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Swing框架之Model</title><link>http://www.blogjava.net/Swing/archive/2007/10/12/152370.html</link><dc:creator>zht</dc:creator><author>zht</author><pubDate>Fri, 12 Oct 2007 08:16:00 GMT</pubDate><guid>http://www.blogjava.net/Swing/archive/2007/10/12/152370.html</guid><wfw:comment>http://www.blogjava.net/Swing/comments/152370.html</wfw:comment><comments>http://www.blogjava.net/Swing/archive/2007/10/12/152370.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Swing/comments/commentRss/152370.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Swing/services/trackbacks/152370.html</trackback:ping><description><![CDATA[<span style="color: #008000">&nbsp;&nbsp; <font size="2">构建应用程序应该</font><font size="2">以数据为中心，而不是以用户界面为中心，这是一个良好的编程习惯。为支持这种编程范式，Swing为每种带有逻辑数据或值的组件定义了独立的模型接口，这种分割使程序可以选择向Swing组件中嵌入自己的模型实现。</font> </span>
<p><span style="color: #008000"><span style="font-family: Georgia"><span style="font-family: Georgia">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下面表格列出Swing中组件及其模型的映射关系：</span></span></span></p>
<table cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td><span style="color: #008000">组件</span></td>
            <td><span style="color: #008000">Model接口</span></td>
            <td><span style="color: #008000">Model类型</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JButton</span></td>
            <td><span style="color: #008000">ButtonModel</span></td>
            <td><span style="color: #008000">GUI状态</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JToggleButton</span></td>
            <td><span style="color: #008000">ButtonModel</span></td>
            <td><span style="color: #008000">GUI状态/应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JCheckBox</span></td>
            <td><span style="color: #008000">ButtonModel</span></td>
            <td><span style="color: #008000">GUI状态/应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JRadioButton</span></td>
            <td><span style="color: #008000">ButtonModel</span></td>
            <td><span style="color: #008000">GUI状态/应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JMenu</span></td>
            <td><span style="color: #008000">ButtonModel</span></td>
            <td><span style="color: #008000">GUI状态</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JMenuItem</span></td>
            <td><span style="color: #008000">ButtonModel</span></td>
            <td><span style="color: #008000">GUI状态</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JCheckBoxMenuItem</span></td>
            <td><span style="color: #008000">ButtonModel</span></td>
            <td><span style="color: #008000">GUI状态/应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JRadioButtonMenuItem</span></td>
            <td><span style="color: #008000">ButtonModel</span></td>
            <td><span style="color: #008000">GUI状态/应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JComboBox</span></td>
            <td><span style="color: #008000">ComboBoxModel</span></td>
            <td><span style="color: #008000">应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JProgressBar</span></td>
            <td><span style="color: #008000">BoundedRangeModel</span></td>
            <td><span style="color: #008000">GUI状态/应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JScrollBar</span></td>
            <td><span style="color: #008000">BoundedRangeModel</span></td>
            <td><span style="color: #008000">GUI状态/应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JSlider</span></td>
            <td><span style="color: #008000">BoundedRangeModel</span></td>
            <td><span style="color: #008000">GUI状态/应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JTabbedPane</span></td>
            <td><span style="color: #008000">SingleSelectionModel</span></td>
            <td><span style="color: #008000">GUI状态</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JList</span></td>
            <td><span style="color: #008000">ListModel</span></td>
            <td><span style="color: #008000">应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JList</span></td>
            <td><span style="color: #008000">ListSelectionModel</span></td>
            <td><span style="color: #008000">GUI状态</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JTable</span></td>
            <td><span style="color: #008000">TableModel</span></td>
            <td><span style="color: #008000">应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JTable</span></td>
            <td><span style="color: #008000">TableColumnModel</span></td>
            <td><span style="color: #008000">GUI状态</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JTree</span></td>
            <td><span style="color: #008000">TreeModel</span></td>
            <td><span style="color: #008000">应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JTree</span></td>
            <td><span style="color: #008000">TreeSelectionModel</span></td>
            <td><span style="color: #008000">GUI状态</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JEditorPane</span></td>
            <td><span style="color: #008000">Document</span></td>
            <td><span style="color: #008000">应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JTextPane</span></td>
            <td><span style="color: #008000">Document</span></td>
            <td><span style="color: #008000">应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JTextArea</span></td>
            <td><span style="color: #008000">Document</span></td>
            <td><span style="color: #008000">应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JTextField</span></td>
            <td><span style="color: #008000">Document</span></td>
            <td><span style="color: #008000">应用数据</span></td>
        </tr>
        <tr>
            <td><span style="color: #008000">JPasswordField</span></td>
            <td><span style="color: #008000">Document</span></td>
            <td><span style="color: #008000">应用数据</span></td>
        </tr>
    </tbody>
</table>
<p style="font-weight: bold; font-family: Georgia"><span style="color: #008000">Swing模型分类</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing提供的模型分为两大类：GUI状态模型和应用数据模型。</span></p>
<p style="font-family: Georgia"><span style="color: #008000"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GUI状态模型是描述GUI控件可视化状态的接口，如按钮是否按下，或列表中那一项被选中。GUI状态模型通常仅在图形用户界面(GUI)环境中用到。</font><font size="2">通常来说，虽然</font><font size="2">编写使用GUI状态模型分离程序，</font><font size="2">尤其是当多个GUI控件共享状态</font><font size="2">，或当操作一个控件自动更新另一个的值时</font><font size="2">比较有用</font><font size="2">，但GUI状态模型在Swing中并不是必需的，完全可以通过组件顶层方法操作GUI控件的状态，而不必和模型直接交互。</font></span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 应用数据模型是描述具有应用程序含义数据的接口，比如表格中的数据，或列表显示的选项。这些数据模型为Swing提供了一个清晰分割应用程序界面和数据逻辑的强大编程模式。对于以数据为核心的Swing组件，比如JTree和JTable，强烈推荐使用数据模型进行交互。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当然一些组件的模型根据应用场景的不同其分类介于GUI状态模型和应用数据模型之间，比如JSlider和JProgressBar 的BoundedRangeModel。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing的可分离模型接口并没有明确界定GUI状态模型和应用数据模型。这儿所以做此说明，目的是让你更好的理解何时以及为何要需要使用分离的模型。</span></p>
<p style="font-family: Georgia"><font size="2"><span style="color: #008000"><span style="font-weight: bold">共享模型定义</span><br />
</span></font></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 值得注意的是，上文中表格中，许多组件的数据抽象相似，只需一个接口而不用过分泛化时，组件可以共享同一模型定义。共享模型定义允许在不同组件之间自动连接。比如，JSlider和JScrollBar都使用BoundedRangeModel接口，因此可以在一个JScrollBar和一个JSlider之间共享同一个BoundedRangeModel实例，这样它们之间的状态就总是同步的。</span></p>
<p><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://s6.album.sina.com.cn/pic/4b6047bc02000kt5" target="_blank"></a><img height="139" alt="" src="http://www.blogjava.net/images/blogjava_net/swing/1.png" width="288" border="0" /><br />
</p>
<p style="font-weight: bold"><span style="color: #008000"><span style="font-family: Georgia">分离模型编程接口</span></span></p>
<p><span style="color: #008000"><span style="font-family: Georgia">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用模型的Swing组件必须提供访问修改模型的set/get方法，即模型必须是该组件的限定性属性。比如，JSlider使用BoundedRangeModel接口作为它的模型定义，因此它必须提供下面方法：</span></span></p>
<address><font color="#006666"><span style="color: #008000"><span style="font-family: Arial">&nbsp;&nbsp; public BoundedRangeModel getModel()</span><br />
<span style="font-family: Arial">&nbsp;&nbsp; publicvoidsetModel(BoundedRangeModelmodel)</span></span></font></address>
<p><span style="color: #008000"><span style="font-family: Georgia">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所有Swing组件有一个共同点：如果你不设置它的模型，组件会在内部创建/安装一个缺省模型。这些缺省模型类的命名习惯是在接口名称之前加上&#8220;Default&#8221;，比如JSlider的构造函数中初始化一个DefaultBoundedModel对象。</span></span></p>
<address><font style="font-family: Arial" size="2"><span style="color: #008000"><font color="#006666">public JSlider(int orientation, int min, int max, intvalue){<br />
&nbsp; checkOrientation(orientation);<br />
&nbsp;&nbsp; this.orientation = orientation;<br />
&nbsp;&nbsp; this.model = newDefaultBoundedRangeModel(value, 0, min, max);<br />
&nbsp;&nbsp; this.model.addChangeListener(changeListener);<br />
&nbsp; updateUI();<br />
}</font><br />
</span></font></address>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果程序接着调用setModel()，缺省的模型就被替换了，比如下面例子：</span></p>
<address style="font-family: Arial"><span style="color: #008000"><font color="#006666">JSlider slider = new JSlider();<br />
BoundedRangeModel myModel = new DefaultBoundedRangeModel() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void setValue(int n){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("SetValue: "+ n);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.setValue(n);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; });<br />
slider.setModel(myModel);</font><br />
</span></address>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于更复杂的模型（如JTable和JList），Swing还提供一个抽象模型实现，让开发者不需要从头开始创建自己的模型。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如JList的模型接口是ListModel，Swing同时提供了DefaultListModel和AbstractListModel两个类来协助开发者创建自定义的列表模型。</span></p>
<p style="font-weight: bold; font-family: Georgia"><span style="color: #008000">模型改变通知</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当数据或者发生变动时，模型必须通知所有相关方（比如视图）。Swing模型使用前面文章所讲述的事件模型来实现这种触发。Swing中有两种方法发送这种通知：</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 发送轻量级通知，表明状态已经改变，需要Listener通过查询模型，发现什么改变了并做出响应。此方法的优点是单独事件实例能用作该模型的所有通知，同时对于需要频繁通知的事件非常有用（比如JScrollBar被拖动时)。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 发送状态化通知，详细描述模型如何改变。这种方法需要为每个通知创建一个新的事件实例。当通知通过查询模型不能有效地给Listener提供足够的信息时，此方法非常有用。比如当JTable的一列表格数据发生改变时。</span></p>
<img src ="http://www.blogjava.net/Swing/aggbug/152370.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Swing/" target="_blank">zht</a> 2007-10-12 16:16 <a href="http://www.blogjava.net/Swing/archive/2007/10/12/152370.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Swing框架之Component </title><link>http://www.blogjava.net/Swing/archive/2007/10/12/152280.html</link><dc:creator>zht</dc:creator><author>zht</author><pubDate>Fri, 12 Oct 2007 03:39:00 GMT</pubDate><guid>http://www.blogjava.net/Swing/archive/2007/10/12/152280.html</guid><wfw:comment>http://www.blogjava.net/Swing/comments/152280.html</wfw:comment><comments>http://www.blogjava.net/Swing/archive/2007/10/12/152280.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Swing/comments/commentRss/152280.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Swing/services/trackbacks/152280.html</trackback:ping><description><![CDATA[<span style="color: #008000">&nbsp;昨天晚上写完Swing的模型和渲染器之后，觉得对Swing的体系结构还是没有说清楚。Swing的基础体系结构中的四大基本对象Component、 Model、UI Delegate以及Renderer都值得详细解释。Swing的树状组件结构（虽然这是用户界面工具通有的特征）也值得详细解释，因为这是完成某些复杂Swing组件，尤其像JTable、JTree、JList和JComboBox这种复杂组件中编辑功能得关键。此外，Swing / AWT的事件模型如Event Dispatching和Propagation和事件处理线程模型也需要详细解释，理解这部份是编写高效率Swing界面的关键。 </span>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 今天从Swing的四大基本对象Component说起。</span></p>
<p><span style="color: #008000">＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Component在Swing的MVC模型中担任Controller的角色，同时它也是Swing API中代表具体组件的对象。Component在Swing中对外负责提供API接口，对内负责协调控制Model和UI Delegate（有时可能还包括Renderer）的操作，可以说是整个Swing结构的中心角色。为了方便你回忆Swing的MVC模型，特地将上一篇文章中的Swing模型示意图引了过来：</span></p>
<p><span style="color: #008000"><img alt="" src="http://s3.album.sina.com.cn/pic/4b6047bc02000kk2" /></span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Component代表Swing对应用程序提供了如下几类编程接口：</span></p>
<ol>
    <li><span style="color: #008000"><font size="2">用户界面的组件树的创建和修改的方法。这包括组件的添加和删除等操作。</font> </span>
    <li><span style="color: #008000"><font size="2">组件属性访问的方法，比如组件位置、组件前后背景色、组件字体等等。</font> </span>
    <li><span style="color: #008000"><font size="2">组件状态及生命周期的管理的方法，比如隐藏和显示、创建和销毁等等。</font> </span>
    <li><span style="color: #008000"><font size="2">组件位置、大小的管理，包括通过布局管理器的方法。</font> </span>
    <li><span style="color: #008000"><font size="2">组件事件处理接口的管理，包括添加、删除等操作。</font> </span></li>
</ol>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从开发者的角度来看，Component是组件树上的节点、是控制外观和行为的入口、是组件事件的发源地。从Swing组件实现者的角度来看， Component是协调Model和UI Delegate的操作的地方，是低层次事件处理的地方，是高层事件发生的地方，是同父组件和子组件交互的地方。掌握的这些角度，Swing程序员完全可以实现自己的自定义简单组件，当然如需要实现类似于JTable和JTree等复杂的矢量组件，还需要进一步了解Swing的Model和UI Delegate以及Renderer模型。</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于复合型(Composite)组件很简单，这儿要讲述的是如何实现自定义的组件，比如表盘，比如温度计这些没有标准组件可以使用的组件，那么如何自己实现这种自定义组件呢？</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不考虑Model分隔和UI Delegate皮肤分离问题，能够简化自定义Component的模型。总的来说自定义组件需要完成两样基本任务：第一侦听并处理低层事件，根据具体情况改变组件状态，如需要还要发出高级事件；第二，根据当前组件的状态画出当前组件的外观。</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 侦听底层的事件是指侦听类似于mouse、keyboard、focus等事件，然后处理此事件，如果发现此事件带有特定语义，表达某种组件行为，则改变当前的组件状态以记录，并触发某种事件通知应用程序进行处理。举例说明，想象你准备实现一个简单的按钮，你可以通过继承JComponent来完成。你可以在按钮初始化时，注册此按钮的鼠标事件侦听器，以侦听发生自己组件上的鼠标事件。当按钮捕获到鼠标按下时，检查鼠标按下的点是否在按钮有效区域内。如果是，则认为当前是一个按钮按下动作，那么改变按钮的状态为按下去，调用repaint方法通知按钮重画成按下去的状态，然后发出 ActionPerformed的事件，通知注册在此按钮上的应用程序的ActionListener处理这个动作。下面是一个简单示意代码：</span></p>
<address><font color="#006666"><span style="color: #008000"><font size="2">public class MyButton extends Jcomponent implements MouseListener{</font><br />
<font size="2">&nbsp;&nbsp;&nbsp; private String text;</font><br />
<font size="2">&nbsp;&nbsp;&nbsp; private boolean pressed=false;</font><br />
<font size="2">&nbsp;&nbsp;&nbsp; private ArrayList&lt;ActionListener&gt; listeners=new ArrayList&lt;ActionListener&gt;();</font><br />
<font size="2">&nbsp;&nbsp;&nbsp; public MyButton(){</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; addMouseListener(this);//将自己注册为自己的鼠标事件侦听器，监听鼠标事件</font><br />
<font size="2">&nbsp;&nbsp;&nbsp; }</font><br />
<font size="2">&nbsp;&nbsp;&nbsp; ....</font><br />
<font size="2">&nbsp;&nbsp; public void mousePressed(MouseEvent evt){</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Point p=evt.getPoint();</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(getBounds().contains(p)){//判断鼠标落点是否在有效区域内。</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pressed=true; //鼠标点击的含义是按钮被按下！改表按钮状态。</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint();&nbsp;&nbsp;&nbsp; //通知按钮重画，由原来的抬起状态改变成按下状态。</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fireActionPerformed(new ActionEvent(this)); //这是一个按钮动作事件，触发它。</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font><br />
<font size="2">&nbsp;&nbsp; }</font><br />
<font size="2">&nbsp;&nbsp; public void addActionListener(ActionListener listener){</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; listeners.add(listener);</font><br />
<font size="2">&nbsp;&nbsp; }</font><br />
<font size="2">&nbsp;&nbsp; public void removeActionListener(ActionListener listener){</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; listeners.remove(listener);</font><br />
<font size="2">&nbsp;&nbsp; }</font><br />
<font size="2">&nbsp;&nbsp; protected fireActionPerformed(ActionEvent evt){</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(ActionListener listener:listeners){</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; listener.actionPerformed(evt);</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font><br />
<font size="2">&nbsp;&nbsp; }</font><br />
<font size="2">&nbsp;&nbsp; ...</font><br />
<font size="2">&nbsp;&nbsp; //这儿你要覆盖paint方法，实现按钮状态的重画</font><br />
<font size="2">&nbsp;&nbsp; public void paint(Graphics g){</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(pressed){</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //画出按下的样子</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //画出抬起的样子</font><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font><br />
<font size="2">&nbsp;&nbsp; }</font><br />
<font size="2">}</font></span></font></address>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面要注意的是你要自己管理自定义组件的事件监听器，包括addListener和removeListener方法，以及如何触发。这个过程很简单，基本上就是上面的模板来实现添加删除和触发。</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 除了要负责事件的处理和新事件的触发，自定义组件第二个要完成的任务就是要根据组件当前的状态改变重画组件的外观。重画组件的外观只需要覆盖public void paint(Graphics g)方法即可以，在这个方法里，你只需要根据当前的组件状态分别画出当前的组件即可。</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当然除了上面两个基本准则外，不要忘了添加访问你的组件属性的方法，比如，如果上面的按钮是个二元按钮（相当于 JCheckbox/JToggleButton的那种按钮），你可能需要提供isPressed或者setPressed来获取和设置当前按钮的状态。注意，在设置状态按钮变化的访问方法中，比如setPressed，你需要使用repaint方法通知按钮重新渲染(复杂的实现可能包括触发propertyChange事件，这儿从简)：</span></p>
<address><font color="#006666"><span style="color: #008000"><font size="2">public void setPressed(boolean p){</font><br />
<font size="2">&nbsp;&nbsp;&nbsp; pressed=p;</font><br />
<font size="2">&nbsp;&nbsp;&nbsp; repaint();</font><br />
<font size="2">}</font></span></font></address>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 到此为止，你已经能根据上面的两条准则简单的实现你想要的组件了。但是你发现没有，你的按钮状态和外观行为都被堆到了Component （MyButton)中实现了，而且，对于各个平台都是一个样子，不能换皮肤。这对于比较简单、不想要皮肤的组件，可能没有什么，但是对于复杂的组件，比如JTable或者甚至Excel类似的电子表格的那种组件，你把数据（组件状态)和外观堆在这儿实现就严重违反了MVC原则。</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如何简化这种组件的实现呢？使你实现的此种组件容易维护、扩展以及重用，皮肤容易换呢？这就需要Swing结构中的另外三个元素：Model、UI Delegate和Renderer，后面的几个文章将讲述Model、UI Delegate和Renderer帮你逐步实现一个复杂、灵活、可扩展、高效的矢量组件。</span></p>
<p><span style="color: #008000">=====================================================</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 今天这样讲，不知道讲明白没有。当初刚开始学习Swing的时候，还不了解Swing的这种MVC结构，因此当时自己做的自定义组件都是这样写的，没有单独的Model和可以定制的UI Delegate，更不用说Renderer了。但是我觉得自己的这个学习过程，恰恰是人们学习Swing的最佳途径，先简化模型，然后逐步扩展，直到了解Swing模型的全部图像。</span></p>
<font color="#008000">=====================================================================================<br />
昨晚回去后还是觉得Component对象本身说的太简单，想来想去，觉得内容实在是太多，有必要补充两个续文说明Component的其它概念。今天介绍Swing组件paint方法的处理流程，这个流程能使我们理解许多Swing机制。明天续文讲述Swing事件处理器、双缓冲和布局管理器等原理。
<p><font face="Georgia" size="2">＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</font></p>
<p><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing组件的paint方法是内部接口方法，一般用户不要直接调用这个方法，它总是在事件调度线程中调用。一般说来除了系统刷新事件触发这个方法，Component的repaint也触发这个方法的调用。repaint方法常用于当组件状态发生变化时刷新界面使用。repaint方法是Swing中少数几个线程安全的方法，可以在任何线程中调用它。它的原理是往事件队列中post一个PAINT事件。由于事件队列的事件是被事件调度线程同步执行的，所以这个方法总是线程安全的。事件调度线程从PAINT事件中获取事件源组件，从系统申请到图形设备资源后，调用该组件的update方法。update是AWT时代遗留下来的产物，本意是AWT组件画好组件背景后，再调用paint方法画出组件的前景。Swing出现后这个方法就被弃用了，所有逻辑都转到paint方法里。Update只是简单地调用paint方法来完成组件的渲染。老的Java教材上经常可以看到，所谓repaint调度update方法，update接着调用paint方法，自定义组件需要重载paint方法等话语，就是因为这个历史造成的。</font></p>
<p><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上篇文章中的MyButton的paint方法实现是一个非常老式的做法。现在JComponent的实现已经把paint方法改造成可以嵌套多重机制地方，这些机制包括层次渲染、边框、透明背景、双缓冲以及皮肤等。这些机制分别实现不同目的的组件提供了方便。</font></p>
<p><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图形用户界面的组件按照其在组件树上的角色可以分为容器组件和叶组件。Swing模型把叶组件当作是特殊、没有子组件的容器组件，只是JComponent继承Container类，所有Swing组件继承JComponent的原因。</font></p>
<p><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JComponent在paint方法中首先根据组件是否需要使用双缓冲，封装好图形设备对象，然后经过一番处理后调用paintComponent方法画出自身，然后调用paintBorder画出边框，最后调用paintChildren来完成子组件的渲染。</font></p>
<p><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paintComponent意思是画出组件自身，不包括子组件。因此前一文章中的MyButton可以通过覆盖paintComponent方法来完成MyButton的重画。在JComponent实现中，JDK 6的paintComponent的代码为：</font></p>
<address><font face="Arial" color="#006666" size="2">&nbsp;&nbsp;&nbsp; protected void paintComponent(Graphics g) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ui != null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Graphics scratchGraphics = (g == null) ? null : g.create();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ui.update(scratchGraphics, this);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scratchGraphics.dispose();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }</font></address>
<p><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个方法首先检测组件是否安装了UI Delegate，如果安装了就将渲染过程代理给UI Delegate。这儿是嵌入皮肤的地方。JDK 6中JComponent对应的UI Delegate的update方法缺省的实现是：</font></p>
<address><font face="Arial" size="2"><font color="#006666">public void update(Graphics g, JComponent c) {<br />
&nbsp;if (c.isOpaque()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp; g.setColor(c.getBackground());<br />
&nbsp;&nbsp;&nbsp;&nbsp; g.fillRect(0, 0, c.getWidth(),c.getHeight());<br />
&nbsp;}<br />
&nbsp;paint(g, c);<br />
</font>}</font></address>
<p><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以看出，背景透明机制在这儿实现。首先UI Delegate对象判断Component是否背景透明的，如果不是透明的，则使用背景色填充整个Component区域，然后调用paint(g, c)来完成组件在这种LookAndFeel种的渲染。了解了这些后，我们几乎就明白了Swing如何实现背景透明和如何切换皮肤。由于后面的文章还会对UI Delegate和皮肤机制详细描述，这儿就到此为止。</font></p>
<p style="font-family: Georgia"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 目前还不要求实现皮肤，在这种情况下只需要重载paintComponent方法就行了，如果需要背景透明机制，可以模仿上面代码，MyButton的paintComponent可以这样写：</font></p>
<address><font face="Arial" color="#006666">public void paintComponent(Graphics g) {<br />
&nbsp;if (isOpaque()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp; g.setColor(getBackground());<br />
&nbsp;&nbsp;&nbsp;&nbsp; g.fillRect(0, 0, getWidth(), getHeight());<br />
&nbsp;}<br />
&nbsp;if(pressed){//按钮按下去了<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //画出按下的样子<br />
&nbsp;}else{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //画出抬起的样子<br />
&nbsp;}<br />
}</font></address>
<p style="font-family: Georgia"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paintBorder意思是画出组件的边框。Swing所有组件都有边框的概念，就是说可以为任何组件添加各种边框，包括自定义的边框。JDK 6中JComponent的paintBorder的实现是这样的：</font></p>
<address><font face="Arial" color="#006666">protected void paintBorder(Graphics g) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Border border = getBorder();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (border != null) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; border.paintBorder(this, g, 0, 0, getWidth(), getHeight());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
}</font></address>
<p style="font-family: Georgia"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 非常直接，如果自己有border，就将画自己边框的任务代理给了这个border，并传给它图形设备和边界参数。Swing缺省提供了大量的各种各样的边框。同样可以定义自己的边框，实现方法就是继承Border类，Border类中有三个方法要实现，它们的含义如下：</font></p>
<address><font face="Arial" color="#006666">public interface Border<br />
{<br />
&nbsp;&nbsp;&nbsp; //这儿是画出组件边框的地方。<br />
&nbsp;&nbsp;&nbsp; void paintBorder(Component c, Graphics g, int x, int y, int width, int height);<br />
&nbsp; //这儿是定义边框边界的地方，组件可以根据这信息，安排它的内容。<br />
&nbsp;&nbsp;&nbsp; Insets getBorderInsets(Component c);<br />
//边框的背景是不是透明的？不是透明的要负责画出边框的背景。是透明的使用组件的背景。<br />
&nbsp;&nbsp;&nbsp; boolean isBorderOpaque();<br />
}</font></address>
<p style="font-family: Georgia"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这儿实现一个简单的红线边框作为演示：</font></p>
<address><font face="Arial" color="#006666">public class RedLineBorder implements Border{<br />
&nbsp;&nbsp;&nbsp; public void paintBorder(Component c, Graphics g, int x, int y, int width, int height){&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setColor(Color.red);//设置为红色<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.drawRect(x,y, width, height);//画出边框<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; public Insets getBorderInsets(Component c){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new Insets(1,1,1,1); //四周都是1<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; public boolean isBorderOpaque(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false; //背景透明<br />
&nbsp;&nbsp;&nbsp; }<br />
}</font></address>
<p style="font-family: Georgia"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paintChildren完成容器类组件的子组件的渲染。JDK缺省的实现是调用各个自组件的paint方法。一般来说不需要重载这个方法。如果想改变诸如组件Z-order遮挡顺序，可以覆盖这个方法，从相反顺序调用组件的paint方法。</font></p>
<p><font size="2"><span style="font-family: Georgia">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 到这儿我们对Swing的结构有了更深化的理解，UI Delegate机制也已经初露倪端。还有几个重要Swing Component概念或者机制没有讲，明天的续文再对它们做出说明。</span></font></p>
<br />
=================================================================================<br />
<div class="middleSize" id="articleContent">
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing的事件处理过程为：事件调度线程(Event Dispatch Thread)从事件队列(EventQueue)中获取底层系统捕获的原生事件，如鼠标、键盘、焦点、PAINT事件等。接着调用该事件源组件的dispachEvent。该方法过滤出特殊事件后，调用processEvent进行处理。processEvent方法根据事件类型调用注册在这个组件上的相应事件处理器函数。事件处理器函数根据这些事件的特征，判断出用户的期望行为，然后根据期望行为改变组件的状态，然后根据需要刷新组件外观，触发带有特定语义的高级事件。此事件继续传播下去，直至调用应用程序注册在该组件上的处理器函数。下图是这个过程的示意图：</font></p>
<div><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://s9.album.sina.com.cn/pic/4b6047bc02000knw" target="_blank"></a></div>
<div>&nbsp; <a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://s9.album.sina.com.cn/pic/4b6047bc02000kp8" target="_blank"><img alt="" src="http://s9.album.sina.com.cn/pic/4b6047bc02000kp8" border="0" /></a></div>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上图所示意的过程简要说就是：</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">Pump an Event-&gt;Dispatch &amp; Process Event-&gt;MouseListener.mousePressed-&gt;fireActionPerformed-&gt;ActionListener.actionPeformed-&gt;Do database query and display result to a table-&gt;Return from actionPerformed-&gt;Return from fireActionPerformed-&gt;Return from MouseListener.mousePressed-&gt;Pump another Event.</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 事件调度线程在应用程序事件处理函数actionPerformed没有完成之前是不能处理下一个事件的，如果应用程序处理函数是一个时间复杂的任务(比如查询数据库并将结果显示到表格中)，后面包括PAINT事件将在长时间内得不到执行。由于PAINT事件负责将界面更新，所以这就使用户界面失去响应。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 打一个比方，事件处理线程就像进入某城唯一的单行道一样，事件相当于汽车。有种PAINT汽车负责为城市运输非常重要的生活物资。但是有一天，PAINT前面有一辆汽车突然坏掉了，司机下来修车。但是这车太难修，一修就是几天，结果后面的PAINT汽车无法前进，物资无法按时运到城里。市民急了，市长虽然不停的打电话催PAINT公司，但即使PAINT公司多添加几辆车也没用。由于进城的唯一条路被那辆车给占着，所以再多的PAINT车也只能堵在路上。<br />
</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不了解Swing的这种事件处理模型的人往往将时间复杂的任务放在处理函数中完成，这是造成Swing应用程序速度很慢的原因。用户触发这个动作，用户界面就失去了响应，于是给用户的感觉就是Swing太慢了。其实这个错误是程序员造成的，并不是Swing的过失。</font></p>
<blockquote dir="ltr" style="margin-right: 0px">
<p style="margin-bottom: 0cm; font-family: Georgia"><font color="#006666" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 说点题外话，所有采用这种事件模型的用户界面工具都会产生这种问题，包括SWT、GTK、MFC等流行的用户界面工具。之所以只有Swing被误解，主要是和Swing的历史、市场时机、商业宣传策略和心理学相关的。</font></p>
<p style="margin-bottom: 0cm; font-family: Georgia"><font color="#006666" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 首先Swing的历史和市场时机极差。Swing出现早期性能也差、错误也多，而Java程序员脱身于传统图形界面工具，对于Swing这种新的事件处理模型并不太了解，而此时正处于Java第一轮狂热的时期，大家都满怀希望做了大量的Swing应用程序，而这些程序中大量存在这种错误方法。于是市场上涌现了大批的这种程序。自从那个时代，因为这些程序，Swing被贴上了慢的标签。又由于当时的Swing界面也丑，和一般的Windows程序风格炯异，更加深人们的这种印象。这种印象一直持续到现在，像烙印一样深深的刻在人们的脑海里。</font></p>
<p style="margin-bottom: 0cm; font-family: Georgia"><font color="#006666" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其次，Swing还有一个致命的问题，就是没有涌现出一个具有标识性的好程序，这是造成它比SWT印象惨的原因。为什么SWT采用相同的事件处理模型，而获得了速度快的声誉呢？这是因为人们当时对于Java做桌面应用的期望心理达到了低谷，而SWT的出现恰恰是伴随Eclipse出现的，早期的Eclipse的确是在速度快、界面漂亮，这一扫当时人们认为Java慢，Java界面丑陋，Java无法做桌面应用的印象，继而这个印象被加在SWT身上，人们认为Eclipse速度快、漂亮是因为SWT，其实如果你知道Swing/SWT事件处理模型的话，你就明白功劳是Eclipse开发者的，Eclipse界面漂亮其实要归功于Eclipse界面设计专家，他们的高水平造就了这个好的IDE，从而也抬起了SWT的声誉。而Swing的名誉恰恰就被早期Swing低水平开发者给毁了。</font></p>
<p style="margin-bottom: 0cm; font-family: Georgia"><font color="#006666" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 再次, 这和商业宣传策略有关。IBM和Eclipse很懂得市场宣传，人们不是认为Java慢吗，就宣传SWT使用原生组件，人们不是认为Swing丑陋、风格炯异吧，就宣传SWT风格一致性，人们不是认为Java不能做桌面应用吗，就宣传基于SWT的Eclipse。其实这一切的背后原因只是&#8220;人&#8221;的不同，Eclipse的开发者和Swing应用程序的开发者，Swing和SWT技术差异并没有造成那么大的差别，如果是相近能力的人使用他们开发的话，应该能做出相近的产品。这可以从现在Eclipse和NetBeans、Intellij IDEA、JDeveloper和JBuilder看的出来。</font></p>
<p style="margin-bottom: 0cm"><font color="#006666"><font style="font-family: Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最后，人类有一个心理学现象，就是一旦形成对某种事物的印象，很难摆脱旧的认识，有时甚至人们不愿意承认摆在眼前的事实。总而言之，Swing和SWT不同遭遇是因为历史、市场时机、商业宣传策略、心理学的种种原因造成的。</font><br />
</font></p>
</blockquote>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么如何避免这个问题，编写响应速度快的Swing应用程序呢？在SwingWorker的javadoc中有这样两条原则：</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">Time-consuming tasks should not be run on the Event Dispatch Thread. Otherwise the application becomes unresponsive. 耗时任务不要放到事件调度线程上执行，否则程序就会失去响应。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">Swing components should be accessed on the Event Dispatch Thread only. Swing组件只能在事件调度线程上访问。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因此处理耗时任务时，首先要启动一个专门线程，将当前任务交给这个线程处理，而当前处理函数立即返回，继续处理后面未决的事件。这就像前面塞车的例子似的，那个司机只要简单的把车开到路边或者人行道上修理，整个公路系统就会恢复运转。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其次，在为耗时任务启动的线程访问Swing组件时，要使用SwingUtilties. invokeLater或者SwingUtilities.invokeAndWait来访问，invokeLater和invokeAndWait的参数都是一个Runnable对象，这个Runnable对象将被像普通事件处理函数一样在事件调度线程上执行。这两个函数的区别是，invokeLater不阻塞当前任务线程，invokeAndWait阻塞当前线程，直到Runnable对象被执行返回才继续。在前面塞车的例子中，司机在路边修车解决了塞车问题，但是他突然想起来要家里办些事情，这时他就可以打个电话让家里开车来。假如修车不受这件事情的影响，比如叫家人送他朋友一本书，他可以继续修车，这时就相当于invokeLater；假如修车受影响，比如缺少某个汽车零件，叫家人给他送过来，那么在家人来之前，他就没法继续修车，这时就相当于invokeAndWait。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下面举一个例子说明这两点，比如按下查询按钮，查询数据量很大的数据库，并显示在一个表中，这个过程需要给用户一个进度提示，并且能动态显示表格数据动态增加的过程。假设按钮的处理函数是myButton_actionPerformed，则：</font></p>
<address style="margin-bottom: 0cm"><font face="Arial" color="#006666">void myButton_actionPerformed(ActionEvent evt){<br />
&nbsp;&nbsp;&nbsp;&nbsp; new MyQueryTask().start();<br />
}<br />
public class MyQueryTask extends Thread{<br />
&nbsp;&nbsp;&nbsp; public void run(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //查询数据库<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final ResultSet result=...;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; / /显示记录<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(;result.next();){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //往表的Model中添加一行数据，并更新进度条，注意这都是访问组件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SwingUtilities.invokeLater(new Runnable(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; addRecord(result);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; ....<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; void addRecord(ResultSet result){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //往表格中添加数据<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jTable.add....<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //更新进度条<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jProgress.setValue(....);<br />
&nbsp;&nbsp;&nbsp; }<br />
}</font></address>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JDK1.6以后，Swing提供了一个专门的类SwingWorker能帮你解决这个编程范式，你所需要做的就是继承这个类，重载doInBackground，然后在actionPeformed中调用它的execute方法，并通过publish/process方法来更新界面。SwingWorker的主要方法和它们的作用在下面的示意图：</font></p>
<p style="margin-bottom: 0cm"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://s10.album.sina.com.cn/pic/4b6047bc02000knx" target="_blank"><img alt="" src="http://s10.album.sina.com.cn/pic/4b6047bc02000knx" border="0" /></a></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从上面示意图可以看出，SwingWorker实际上不过是封装了前面我所说的例子中的MyQueryTask，并做了更详尽的考虑。execute方法相当于MyQueryTask线程start，它启动这个后台线程并立刻返回。SwingWorker可以注册PropertyChangeListener，这些listener都被在事件调度线程上执行，相当于MyQueryTask中的那些访问组件的Runnable对象。另外，publish、setProgress只不过是特殊的property事件吧，process和done不过是响应publish和PropertyChangeEvent.DONE这个事件的方法罢了。因此我们很容易将上面的例子改成SwingWorker的版本：</font></p>
<address style="margin-bottom: 0cm"><font face="Arial" color="#006666">void myButton_actionPerformed(ActionEvent evt){<br />
&nbsp;&nbsp;&nbsp; new MyQueryTask().execute();<br />
}<br />
<br />
public class MyQueryTask extends SwingWorker{<br />
&nbsp;&nbsp;&nbsp; public void doInBackground(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //查询数据库<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final ResultSet result=...;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //显示记录<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(;result.next();){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //往表的Model中添加一行数据，并更新进度条，注意这都是访问组件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; publish(result);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; public void process(Object ... result){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //往表格中添加数据<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jTable.add....<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //更新进度条<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jProgress.setValue(....);<br />
&nbsp;&nbsp;&nbsp; }<br />
}</font></address>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于一般的耗时任务这样做是比较普遍的，但是有一些任务是一旦触发之后，会周期性的触发，如何做处理这种任务呢?JDK中提供了两个Timer类帮你完成定时任务，一个是javax.swing.Timer，一个java.util.Timer。使用它们的方法很简单，对于Swing的timer，使用方法如下：</font></p>
<address style="margin-bottom: 0cm"><font face="Arial" color="#006666">public void myActionPerformed(){<br />
&nbsp;&nbsp;&nbsp; //假设点击了某个按钮开始记时<br />
&nbsp;&nbsp;&nbsp; Action myAction=new AbstractAction(){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void actionPerformed(ActionEvent e){<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //做周期性的活动，比如显示当前时间<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; Date date=new Date();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jMyDate.setDate(date);//jMyDate是个假想的组件，能显示日期时间<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; };<br />
&nbsp;&nbsp;&nbsp; new Timer(1000, myAction).start();<br />
}</font></address>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.util.Timer类似，只不过使用TimerTask完成动作封装。注意这两个Timer有一个关键的区别：Swing的Timer的事件处理都是在事件调度线程上进行的，因而它里面的操作可以直接访问Swing组件。而java.util.Timer则可能在其他线程上，因而访问组件时要使用SwingUtilities.invokeLater和invokeAndWait来进行。这一点要记住。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果要了解更详细的信息，可以查阅SwingWorker、Swing Timer和util Timer这些类javadoc文档和其他网上资料。最重要的是要记住了那两条原则。<br />
<br />
============================================================================<br />
</p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="3"><strong>Swing事件与事件处理器模型</strong></font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Component在Swing模型中是事件触发源。前一篇文章在描述Swing的事件处理模型时就已经提到了这个事件处理过程。简单来说，Swing组件在侦听到原生事件并处理后，往往产生新的逻辑事件。逻辑事件是某些组件所特有的、具有特定语义的事件，比如JButton按下时产生ActionEvent、JComboBox一项被选中时产生ItemEvent，等等。和原生事件不同，它们并不被派发到系统事件队列中，而是由组件直接触发。事件处理器作为组件的观察者添加到组件上并侦听触发的事件。假设事件名叫XXX，Swing中实现这个模式的一般模式是：</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">1.定义一个XXXEvent</font></p>
<address style="margin-bottom: 0cm"><font color="#006666"><font face="Arial" size="2">public class XXXEvent extends Event{</font><br />
<font face="Arial" size="2">&nbsp;&nbsp;&nbsp; ...</font><br />
<font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">public void XXXEvent(Object src){</font><br />
<font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">super(src);</font><br />
<font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">...</font><br />
<font face="Arial" size="2">&nbsp;&nbsp;&nbsp;&nbsp;</font><font face="Arial" size="2">}</font><br />
<font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font face="Arial" color="#006666" size="2">...<br />
}</font></address>
<blockquote dir="ltr" style="margin-right: 0px">
<blockquote dir="ltr" style="margin-right: 0px"></blockquote></blockquote>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">2.定义一个事件处理器接口XXXListener，声明所有和该事件相关的处理方法：</font></p>
<address style="margin-bottom: 0cm"><font color="#006666"><font face="Arial" size="2">public interface XXXListener extends EventListener{</font><br />
</font><font color="#006666"><font face="Arial" size="2">&nbsp;&nbsp;&nbsp; void action1(XXXEvent evt);</font></font> <font face="Arial" size="2"><br />
</font><font color="#006666"><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">void action2(XXXEvent evt);</font></font> <font face="Arial" size="2"><br />
</font><font color="#006666"><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font face="Arial" color="#006666" size="2">...<br />
}</font></address>
<blockquote dir="ltr" style="margin-right: 0px"></blockquote>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">3.在触发它的组件中定义一下方法：</font></p>
<blockquote dir="ltr" style="margin-right: 0px">
<blockquote dir="ltr" style="margin-right: 0px"></blockquote>
<blockquote dir="ltr" style="margin-right: 0px"></blockquote>
<blockquote dir="ltr" style="margin-right: 0px">
<blockquote dir="ltr" style="margin-right: 0px"></blockquote></blockquote>
<blockquote dir="ltr" style="margin-right: 0px">
<blockquote dir="ltr" style="margin-right: 0px"></blockquote></blockquote>
<blockquote dir="ltr" style="margin-right: 0px">
<blockquote dir="ltr" style="margin-right: 0px"></blockquote>
<blockquote dir="ltr" style="margin-right: 0px"></blockquote></blockquote></blockquote>
<address style="margin-bottom: 0cm"><font color="#006666"><font face="Arial" size="2">public class MyComponent extends Jcomponent{<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">...<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">//存放事件处理器的队列<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">private ArrayList&lt;XXXListener&gt;xxxListeners=new ArrayList&lt;XXXListener&gt;();<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">//定义以下各种方法，访问符号用public，以方便添加删除处理器<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">public void addXXXListener(XXXListener listener){<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">xxxListeners.add(listener);<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">}<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">public void removeXXXListener(XXXListener listener){<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">xxxListeners.remove(listener);<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">}<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">//定义各种触发(fire)action1、action2...的方法，注意一般使用protected，以便继承和扩展<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">//每一个action都要定义一个相应触发(fire)的方法<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">protected void fireAction1(XXXEvent evt){<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">for(XXXListener listener:xxxListeners){<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">listener.action1(evt);<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">}<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">}<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">protected void fireAction2(XXXEvent evt){<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">for(XXXListener listener:xxxListeners){<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">listener.action2(evt);<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">}<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">}<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">...<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">//在某些地方，比如鼠标处理函数中触发相应的动作<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">void myMouseReleased(MouseEvent evt){<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">...<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">if(应该触发action1)<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">fireAction1(new XXXEvent(this));<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">...<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">if(应该触发action2)<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">fireAction2(new XXXEvent(this));<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font> <font face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font face="Arial" size="2">...<br />
</font><font face="Arial" size="2">&nbsp;&nbsp;&nbsp;&nbsp;</font></font><font face="Arial" color="#006666" size="2">}<br />
}</font></address>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XXXEvent、XXXListener、addXXXListener、removeXXXListener以及各种fireAction函数多是重复性代码，有些Java IDE如JBuilder中能够根据开发者的指定参数的自动生成这些代码。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 实际上这个观察者模式的编程范式可以推广到任何JavaBeans，不一定是可视化的Swing组件。以前曾经见过JBuilder做的一个所谓数据库操作的JavaBeans，它没有界面，但它和Swing组件完全一样添加删除处理器。它的功能是异步操作数据库，在数据操作完了之后触发注册在上面的事件处理器，该事件处理器就可以将查询结果展现在表格中，或者输出成报表等等。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在这个模型中，JavaBeans本身既可以是事件源（被观察对象），也可以是事件处理器（观察者），JavaBeans也可以侦听自身的事件并且处理。比如前面文章所提的MyButton在处理鼠标事件时就是自己侦听自己发出的鼠标事件，自己既是事件源，又是事件处理器，形成自反系统。各种各样的JavaBeans通过这种机制联系成一张事件网，各种JavaBeans就是这个网上的节点，而它们之间的事件触发与事件处理关系就是这张网络上的线。当某个节点被外界或自身发出的事件所触发时，行成了事件的传播。这个过程很像网络上节点的振动引起周围周围节点振动的模型。下图示意了这种JavaBeans之间的事件网：</font></p>
<p style="margin-bottom: 0cm"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://s11.album.sina.com.cn/pic/4b6047bc02000kqm" target="_blank"><font face="Georgia" size="2"><img src="http://s11.album.sina.com.cn/pic/4b6047bc02000kqm" border="0"  alt="" /></font></a></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 例如new JscrollPane(new JtextArea())这个系统，它里面包括两个JScrollBar和一个JTextArea，当鼠标拖动事件触发JScrollBar时，JScrollBar处理了这个鼠标拖动事件，并发出滚动条拖动事件，这个事件传播给JTextArea，JTextArea处理这个拖动事件，相应的更新自己显示的内容，如果JTextArea之后又根据更新发出了一个新的事件，这个事件便会继续传播下去。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="3"><strong>Swing布局管理器</strong></font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现在高级图形用户界面工具一般都包括布局管理器机制。什么叫做布局管理器？如果所有窗口的大小是不变的，那么我们在往窗口中添加组件时，只要将组件的拖放到固定位置、调整好尺寸就可以了，就像VB的界面工具一样。可大多数情况并非如此，用户经常需要调整窗口的大小，以便和其他程序协同工作。这种情况下，在传统界面工具中，比如VB，就需要显式的侦听窗口尺寸调整事件，根据当前窗口的大小重新计算并调整各个组件的大小和位置。AWT/SWT/Swing将这个过程自动化、模块化了，抽象出一个布局管理器来负责管理界面组件的布局。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 它们实现原理是相似的：容器类组件侦听初始化、invalide/validate以及容器尺寸调整等事件，一旦发生这些事件，容器类组件检查自己是否配置了布局管理器，如果没有，则不做任何事情；如果有，则将容器内组件的布局代理给布局管理器，让它来完成容器内组件的重新布局。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 容器管理器对象对实现两类接口：LayoutManager和LayoutManager2，LayoutManager2是LayoutManager的一个扩展，允许组件在添加时指定位置参数。它们的定义和含义如下：</font></p>
<blockquote dir="ltr" style="margin-right: 0px"></blockquote>
<blockquote dir="ltr" style="margin-right: 0px"></blockquote>
<address style="margin-bottom: 0cm"><font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">public interface LayoutManager {<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">//添加组件comp，并和name关联起来，name可以作为位置等特殊含义参数来使用<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">void addLayoutComponent(String name, Component comp);<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">//删除组件comp<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">void removeLayoutComponent(Component comp);<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">//根据容器内的当前组件，计算容器parent的最优尺寸。<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">Dimension preferredLayoutSize(Container parent);<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">//根据容器内的当前组件，计算容器parent的最小尺寸。<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">Dimension minimumLayoutSize(Container parent);<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">//重新布局容器parent，这儿是主要布局逻辑所在。<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">void layoutContainer(Container parent);<br />
}<br />
public interface LayoutManager2 extends LayoutManager {<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">//添加组件comp，constraints用作指定如何以及位置的参数，这个函数主要是弥补LayoutManager版的addLayoutComponent表达能力欠缺而添加。<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">void addLayoutComponent(Component comp, Object constraints);<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">//根据容器内的当前组件，计算容器parent的最大尺寸。看来除了最优、最小，某些情况下还是需要知道最大。<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">public Dimension maximumLayoutSize(Container target);<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">//指定水平方向上组件之间的相对对齐方式，0表示和源组件对齐，1表示远离源组件。<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">public float getLayoutAlignmentX(Container target);<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">//指定垂直方向上组件之间的相对对齐方式，0表示和源组件对齐，1表示远离源组件。<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">public float getLayoutAlignmentY(Container target);<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font color="#006666"><font style="font-family: Arial" face="Georgia" size="2">//invalidate这个布局管理器，有时布局管理器为了计算迅速，可能第一次计算之后就将一些数据给缓冲，但是后容器内的组件数目发生变化，这儿的缓冲值就需要调用这个方法通知更新<br />
</font><font style="font-family: Arial" face="Arial" size="2">&nbsp;&nbsp;&nbsp;</font></font> <font face="Georgia" size="2"><font color="#006666"><span style="font-family: Arial">public void invalidateLayout(Container target);</span><br style="font-family: Arial" />
<br />
<br />
<span style="font-family: Arial">}</span></font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing在java.awt和javax.swing中都分别提供大量的布局管理器，这些布局管理器有简单的如FlowLayout，有复杂的如GridBadLayout。用户还可以自己定义自己的布局管理器，由于篇幅原因，这儿略去例子。</font></address>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Java 6中在布局管理中引入了BaseLine / Anchor的概念，能协助Java IDE的用户界面设计工具，方便用户来设计布局组件。NetBeans的Matisse组件首先引入了一个GroupLayout布局管理器，结合Matisse使用，提供了非常方便的布局管理和界面设计。GroupLayout和BaseLine/Anchor概念以及Matisse可以说是Java界面设计工具的一大进步，可以说足以成为Java桌面应用史上的一个里程碑。在这之前，缺乏有力的界面设计工具是Java在桌面应用失败的一个重要原因。虽然Anchor概念早就在Delphi界面设计工具出现过，但是这个工具的出现还是Java界面设计史上的一大事件。随着Java 6桌面应用支持的增强，以及NetBeans Matisse之类界面设计工具的出现，使得Java桌面应用时代已经到来。Seeing is believing，你不妨试一下就知道了。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本想再加一节讲述Swing双缓冲机制，但是想到双缓冲并不是Swing模型的核心概念，没有它并不影响理解Swing的总体模型，因此打算把它作为以后的一篇专门技术文章来写。</font></p>
<p style="margin-bottom: 0cm"><font face="Georgia" size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样Swing模型中的Component部分就算是描述完了，从明天开始，讲述Swing模型中的另外三个重要概念：Model、UI Delegate和Renderer。</font></p>
<p style="margin-bottom: 0cm"><br />
</font></font></p>
<br />
</div>
<img src ="http://www.blogjava.net/Swing/aggbug/152280.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Swing/" target="_blank">zht</a> 2007-10-12 11:39 <a href="http://www.blogjava.net/Swing/archive/2007/10/12/152280.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AWT/SWT/Swing大比较之一：模型设计与实现 </title><link>http://www.blogjava.net/Swing/archive/2007/10/12/152244.html</link><dc:creator>zht</dc:creator><author>zht</author><pubDate>Fri, 12 Oct 2007 02:10:00 GMT</pubDate><guid>http://www.blogjava.net/Swing/archive/2007/10/12/152244.html</guid><wfw:comment>http://www.blogjava.net/Swing/comments/152244.html</wfw:comment><comments>http://www.blogjava.net/Swing/archive/2007/10/12/152244.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Swing/comments/commentRss/152244.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Swing/services/trackbacks/152244.html</trackback:ping><description><![CDATA[<span style="color: #008000">&nbsp; 前几天由于网络问题，访问不了新浪网，现在准备重新开始博客写作。最近打算写的内容主要包括Java性能、Swing和SWT的比较、Swing方面的一些技术。 </span>
<p style="font-family: Georgia"><span style="color: #008000">=====================================================</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 总的来说Swing/AWT和SWT在事件处理机制上是类似的，窗口组件的树状结构也是类似的。图形用户界面系统在事件处理设计上有两大类，一类是单线程模型，一类是多线程模型。在事件处理机制上，三者都是遵循单线程规则。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 单线成模型对于事件处理不保证线程安全性(Thread Safety)，所有的事件处理都在Event Dispatch Thread(EDT)上进行，此一类事件模型通常叫做单线程模型。这种模型规定所有对组件的访问操作必须在EDT上完成。为什么对于组件的访问需要在EDT上完成？这主要是为了保证对于组件状态的改变是同步的，保证了界面组件的可确定性。这中模型是大部分图形用户界面工具采用的模型，包括Swing/AWT、SWT、GTK、WinForm等等。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这种模型的好处是，结构设计和代码实现都比较简单，避免了为了实现线程同步的复杂处理。但是也带来了一些问题，最常见的问题是，程序员容易将长时间复杂任务的处理放在事件处理函数完成，造成EDT线程被阻塞，给用户造成界面失去响应的错觉。其实人们对于Swing速度慢和反映迟钝的感觉大部分来源于此，简单的说，是程序员的问题，而不是Swing自身的问题，是因为程序员没有理解这种事件处理机制造成的。其实在SWT、GTK、WinForm等任何以这种事件模型为基础的工具都会出现。重现的方法就是你简单的将长时间处理的任务放在事件处理函数中，你的用户界面就会失去响应。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如何解决这种问题？通用的办法就是采用异步线程处理长时间任务。但是还要记住的是，在这种任务中对于界面的更新要采用SwingUtilities.invokeLater或者在SWT采用Synchronize方法，将访问操作放到EDT上进行。关于如何写一个有效处理长时间任务的Swing程序，将会在其他文章中描述。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 多线程模型中，所有的事件处理都是在异步线程中进行，界面组件同步访问的操作需要程序员来保证。这种模型设计本身很复杂，而且对于程序员来说要求比较高，必须对线程同步编程很熟悉，而且花在同步上的操作影响了平台的性能。一般现在的图形界面工具都不再采用这种方式。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下面比较一下Swing/AWT/SWT在API、GUI特征以及实现方法的不同。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在API上，Swing和AWT是兼容的，SWT是单独的一套接口。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">1.Swing/AWT的组件在生成时可以脱离父组件独立存在，SWT必须有父组件存在。这主要是由于SWT的资源是自己管理，SWT程序必须负责释放不用的资源，为了避免这种释放资源的重复性，SWT父组件被设计成在析构时自动递归调用子组件的析构函数。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">2.Swing/AWT的资源回收由垃圾收集器负责，SWT必须由SWT程序显式释放。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">3.Swing/AWT的事件线程循环不需要程序员显式启动，SWT必须要程序员来显式启动。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing/AWT和SWT在布局管理器上是类似的，没有太大区别。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在GUI特征上，有两个比较层面，一个是组件种类，一个是组件本身特征。在理解这些特征时，一定要牢牢记住这样一个准则：Java是平台无关的语言，因此它对应用程序所提供的API一定要各个平台都相同，GUI特征其实也是API，所以GUI的特征必须在各个平台都相同。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 组件类型上，AWT采用的是最大公约数方法，而Swing/SWT采用的是最小公倍数方法。简单的说AWT是各个平台所有组件集合的交集，而Swing和SWT则是各个平台组件的并集。下图所示，假设操作系统平台OS1上提供组件{C1, C2, C3, C4, C7}，而OS2提供{C1, C2,C3, C4, C6}，OS3提供{C1, C2, C3, C4, C5}，那么其中的阴影部分就是AWT所实现的组件，由于C5、C6和C7是各个平台所特有的，因此AWT中就不包含这些组件。比如Table和Tree在Java支持某些操作系统平台中不包含，所以在AWT中就没有Table和Tree。由于AWT的组件太贫乏，所以AWT在现在复杂应用程序几乎没有什么用。Swing和SWT提供的组件是各平台所有组件的并集，这样就解决AWT的组件贫乏的缺陷。也就是说，Swing和SWT提供的组件包括C1到C7的所有组件，而AWT只提供C1到C4的所有组件。</span></p>
<p style="font-family: Georgia"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://s12.album.sina.com.cn/pic/4b6047bc02000kdb" target="_blank"><span style="color: #008000"><img title="http://s12.album.sina.com.cn/pic/4b6047bc02000kdb" alt="http://s12.album.sina.com.cn/pic/4b6047bc02000kdb" src="http://s12.album.sina.com.cn/pic/4b6047bc02000kdb" border="1" /></span></a></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从组件本身的特征来看，SWT和AWT采用了相同的策略，即最大公约数，而Swing采用的是最小公倍数。如下图所示，假设对于同一个组件C，如果它在OS1上提供的特征包括{a,b,c,d,e}，而OS2上提供的特征包括{a,b,c}，而OS3包括的特征有{a,b,c,d}，那么SWT和AWT提供的组件特征只包括{a,b,c}，而Swing的包含的平台特征包括{a, b, c, d, e}。比如由于Solaris平台上的按钮不提供对于图标的支持，所以SWT和AWT的独立按钮就不提供对于按钮图标的支持，而Swing提供按钮图标的支持。</span></p>
<p style="font-family: Georgia"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://s1.album.sina.com.cn/pic/4b6047bc02000kdc" target="_blank"><span style="color: #008000"><img title="http://s1.album.sina.com.cn/pic/4b6047bc02000kdc" alt="http://s1.album.sina.com.cn/pic/4b6047bc02000kdc" src="http://s1.album.sina.com.cn/pic/4b6047bc02000kdc" border="1" /></span></a></p>
<p style="margin-bottom: 0cm; font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在实现方法上，AWT采用Java+Native C Peer(一种JNI调用）方法，SWT根据各平台的不同情况，一部分组件使用Java+Java Peer+JNI Wrapper，一部分采用Java模拟的方法，而Swing则采用所有组件都纯粹Java模拟的方法。</span></p>
<p style="margin-bottom: 0cm; font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AWT的接口和各操作系统组件之间的差别采用Native C Peer实现的方法来填平，下面是它的结构示意图：</span></p>
<p style="font-family: Georgia"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://s2.album.sina.com.cn/pic/4b6047bc02000kdd" target="_blank"><span style="color: #008000"><img title="http://s2.album.sina.com.cn/pic/4b6047bc02000kdd" alt="http://s2.album.sina.com.cn/pic/4b6047bc02000kdd" src="http://s2.album.sina.com.cn/pic/4b6047bc02000kdd" border="1" /></span></a></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其中AWT Native Peer Impl部分都是C语言写的，在各种操作系统上是不同的，但是它们和AWT Component组件之间的AWT-Peer JNI调用接口是不变的，AWT Component的Java代码实现在各个平台上都是相同的，最后AWT Component向Application提供同一的API接口。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SWT同AWT不同，它在Native Component上进行了一层薄薄的JNI封装，所有操作系统的API调用都被映射到一个JNI调用上，然后SWT通过Java代码组合这些JNI调用实现同一的API，下面是其结构示意图：</span></p>
<p style="font-family: Georgia"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://s3.album.sina.com.cn/pic/4b6047bc02000kde" target="_blank"><span style="color: #008000"><img title="http://s3.album.sina.com.cn/pic/4b6047bc02000kde" alt="http://s3.album.sina.com.cn/pic/4b6047bc02000kde" src="http://s3.album.sina.com.cn/pic/4b6047bc02000kde" border="1" /></span></a></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因此SWT的Java代码实现部分在各个平台是不同的，它的C代码部分即JNI Wrapper部分只是一个各平台GUI API的JNI简单映射，SWT通过Java Peer在各平台的不同实现填平了各平台差异，从而给Application提供同一的API接口。当然SWT对于某种平台上缺少的组件采用的方法和Swing基本类似。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing和上两中方式完全不同，它直接调用Java2D，抛弃了本地操作系统平台组件的实现，完全自己画出来了整个组件，当然Java2D底层也是调用平台的图形系统。下面是它的示意图：</span></p>
<p style="font-family: Georgia"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://s4.album.sina.com.cn/pic/4b6047bc02000kdf" target="_blank"><span style="color: #008000"><img title="http://s4.album.sina.com.cn/pic/4b6047bc02000kdf" alt="http://s4.album.sina.com.cn/pic/4b6047bc02000kdf" src="http://s4.album.sina.com.cn/pic/4b6047bc02000kdf" border="1" /></span></a></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当然Swing是建立在AWT基础上的，对于一些顶层容器类如Frame / Dialog / Window以及Applet是直接采用AWT的，这儿为了方便并没有画出。由于Java2D API是个平台无关的，因此Swing的Java实现代码在个平台都是一样的，都是一套，当然除了与Swing的Look And Feel相关的东西以外。</span></p>
<p style="font-family: Georgia"><span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 由于篇幅原因，今天就先谈到这儿，至于这三种不同架构、实现会对它们的性能和外观以及编程难度产生什么影响，我们以后的文章逐渐讨论。</span></p>
<div class="invisible" id="reference"><span style="color: #008000">文章引用自：</span><a href=" ?></a><span style=" target=" COLOR: #008000"> </span></div>
<img src ="http://www.blogjava.net/Swing/aggbug/152244.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Swing/" target="_blank">zht</a> 2007-10-12 10:10 <a href="http://www.blogjava.net/Swing/archive/2007/10/12/152244.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Swing编写全屏程序 </title><link>http://www.blogjava.net/Swing/archive/2007/10/12/152240.html</link><dc:creator>zht</dc:creator><author>zht</author><pubDate>Fri, 12 Oct 2007 02:02:00 GMT</pubDate><guid>http://www.blogjava.net/Swing/archive/2007/10/12/152240.html</guid><wfw:comment>http://www.blogjava.net/Swing/comments/152240.html</wfw:comment><comments>http://www.blogjava.net/Swing/archive/2007/10/12/152240.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Swing/comments/commentRss/152240.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Swing/services/trackbacks/152240.html</trackback:ping><description><![CDATA[<span style="color: #008000">&nbsp;也许用到的机会很少，但JDK还是为我们提供了这个的功能。像许多软件中的打印预览功能，还有某些文本编辑器中为了获得更大的编辑画面，也用到了全屏幕模式，如果你有兴趣写一个像ACDSee这样的软件，使用全屏幕模式可以让用户看到更大的图片画面。 </span>
<p><span style="color: #008000"><span style="font-family: Georgia">　　如何使用全屏幕模式？</span></span></p>
<p><span style="color: #008000"><span style="font-family: Georgia">　　关键是java.awt.*里面的两个与显示设备有关的类：GraphicsEnvironment和GraphicsDevice。</span></span></p>
<p><span style="color: #008000"><span style="font-family: Georgia">　　GraphicsEnvironment为Java应用程序提供了特定平台的 GraphicsDevice对象和 Font 对象集合。这些GraphicsDevice可以是各种本机和远端机器的资源，如屏幕、打印机或者是Image Buffer,甚至是Graphics2D绘图方法的目标对象。</span></span></p>
<p><span style="color: #008000"><span style="font-family: Georgia">　　而GraphicsDevice就是指特定的图形环境了，如屏幕和打印设备等。这样，我们就可以用GraphicsDevice来操纵屏幕了。GraphicsDevice提供的setFullScreenWindow()方法就是设置全屏幕用的。</span></span></p>
<p><span style="color: #008000"><span style="font-family: Georgia">　　由于GraphicsEnvironment的构造器是受保护的(protected)，我们不能直接构造一个GraphicsEnvironment 对象来获得GraphicsDevice对象。幸好它提供了getLocalGraphicsEnvironment()方法，用来获得一个 GraphicsEnvironment实例：</span></span></p>
<p><span style="color: #008000"><span style="font-family: Georgia">　　GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();</span></span></p>
<p><span style="color: #008000"><span style="font-family: Georgia">　　有了GraphicsEnvironment可以调用getDefaultScreenDevice方法获得当前的屏幕设备了：</span></span></p>
<p><span style="color: #008000"><span style="font-family: Georgia">　　GraphicsDevice gd = ge.getDefaultScreenDevice();</span></span></p>
<p><span style="color: #008000"><span style="font-family: Georgia">　　自己动手体验一下</span></span></p>
<p><span style="color: #008000"><span style="font-family: Georgia">　　有了上面的简介，写一个实例来体验一下吧：</span></span></p>
<p><font size="2"><span style="color: #008000"><span style="font-family: Arial">import java.awt.Color;</span><br />
<span style="font-family: Arial">import java.awt.Font;</span><br />
<span style="font-family: Arial">import java.awt.Graphics;</span><br />
<span style="font-family: Arial">import java.awt.GraphicsDevice;</span><br />
<span style="font-family: Arial">import java.awt.GraphicsEnvironment;</span><br />
<span style="font-family: Arial">import java.awt.event.MouseAdapter;</span><br />
<span style="font-family: Arial">import java.awt.event.MouseEvent;</span><br />
<span style="font-family: Arial">import javax.swing.JWindow;</span><br />
<span style="font-family: Arial">public class FullScreenTest {</span><br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp; public static void main(String[] args) {</span><br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GraphicsEnvironment ge =</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GraphicsEnvironment.getLocalGraphicsEnvironment();</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GraphicsDevice gd = ge.getDefaultScreenDevice();</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FullScreenWindow myWindow = new FullScreenWindow();</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( gd.isFullScreenSupported() )</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gd.setFullScreenWindow(myWindow);</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Unsupported full screen.");</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp; }</span><br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp; static class FullScreenWindow extends JWindow {</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public FullScreenWindow() {</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.addMouseListener(new MouseAdapter() {</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void mousePressed(MouseEvent evt) {</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; quit();</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void quit() {</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.dispose();</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void paint(Graphics g) {</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setFont(new Font("Arial",Font.BOLD,30));</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.setColor(Color.RED);</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g.drawString("这是全屏幕模式",100,100);</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">&nbsp;&nbsp;&nbsp; }</span><br style="font-family: Arial" />
<br />
<span style="font-family: Arial">}</span></span></font></p>
<div class="invisible" id="reference"><span style="color: #008000">文章引用自：</span><a href=" ?></a><span style=" target=" COLOR: #008000"> </span></div>
<img src ="http://www.blogjava.net/Swing/aggbug/152240.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Swing/" target="_blank">zht</a> 2007-10-12 10:02 <a href="http://www.blogjava.net/Swing/archive/2007/10/12/152240.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一些小例子(Ing)</title><link>http://www.blogjava.net/Swing/archive/2007/09/14/145073.html</link><dc:creator>zht</dc:creator><author>zht</author><pubDate>Fri, 14 Sep 2007 02:49:00 GMT</pubDate><guid>http://www.blogjava.net/Swing/archive/2007/09/14/145073.html</guid><wfw:comment>http://www.blogjava.net/Swing/comments/145073.html</wfw:comment><comments>http://www.blogjava.net/Swing/archive/2007/09/14/145073.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Swing/comments/commentRss/145073.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Swing/services/trackbacks/145073.html</trackback:ping><description><![CDATA[<span style="color: #008000"><span style="color: #ff0000">平时别人问得一些小例子，做出来放在这，以后不断添加<br />
需要的也可以留言提出。<br />
</span><br />
1、使用Insert来判断JTextField是插入模式还是修改模式<br />
</span>
<p><span style="color: #008000">public class InsertTextField extends JPanel {</span></p>
<p><span style="color: #008000">&nbsp;/**<br />
&nbsp; * @param args<br />
&nbsp; */<br />
&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;JFrame jf = new JFrame();<br />
&nbsp;&nbsp;jf.setContentPane(new InsertTextField());<br />
&nbsp;&nbsp;jf.pack();<br />
&nbsp;&nbsp;jf.setVisible(true);<br />
&nbsp;}<br />
&nbsp;public InsertTextField() {<br />
&nbsp;&nbsp;this.setLayout(new FlowLayout());<br />
&nbsp;&nbsp;final JTextField jtf = new JTextField(10);<br />
&nbsp;&nbsp;final IntegerDocument id = new IntegerDocument(10);<br />
&nbsp;&nbsp;jtf.addKeyListener(new KeyListener() {<br />
&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;public void keyPressed(KeyEvent e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (e.getKeyCode() == KeyEvent.VK_INSERT) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id.setFlag();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;public void keyReleased(KeyEvent e) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;jtf.setCaretPosition(jtf.getText().length());<br />
&nbsp;&nbsp;&nbsp;}</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;public void keyTyped(KeyEvent e) {</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;}</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;});<br />
&nbsp;&nbsp;jtf.setDocument(id);<br />
&nbsp;&nbsp;this.add(jtf);<br />
&nbsp;}</span></p>
<p><span style="color: #008000">&nbsp;class IntegerDocument extends PlainDocument {</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;private boolean flag = false;</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;public IntegerDocument(int n) {<br />
&nbsp;&nbsp;&nbsp;super();<br />
&nbsp;&nbsp;}</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;public void setFlag() {<br />
&nbsp;&nbsp;&nbsp;boolean f = flag ? flag = false : (flag = true);<br />
&nbsp;&nbsp;}</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;public void insertString(int offset, String s, AttributeSet ats)<br />
&nbsp;&nbsp;&nbsp;&nbsp;throws BadLocationException {</span></p>
<p><span style="color: #008000">&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;super.insertString(offset, s, ats);<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (flag) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remove(offset - 1, 1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;} catch (Exception ex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;Toolkit.getDefaultToolkit().beep();<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;}<br />
}<br />
2、拖动JPanel，还有一些小bug，以后有空再改<br />
</p>
<p>/**<br />
&nbsp;* DragPanel Class<br />
&nbsp;* <br />
&nbsp;* author:zhangtao<br />
&nbsp;* mail:zht_dream@hotmail.com<br />
&nbsp;* 2007-9-26<br />
&nbsp;*/<br />
public class DragPanel extends JPanel implements MouseListener,<br />
&nbsp;&nbsp;MouseMotionListener {</p>
<p>&nbsp;JPanel north_JP;</p>
<p>&nbsp;JScrollPane cen_JP;</p>
<p>&nbsp;/**<br />
&nbsp; * @param args<br />
&nbsp; */<br />
&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;JFrame jf = new JFrame();<br />
&nbsp;&nbsp;jf.setContentPane(new DragPanel());<br />
&nbsp;&nbsp;jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />
&nbsp;&nbsp;jf.pack();<br />
&nbsp;&nbsp;jf.setVisible(true);<br />
&nbsp;}</p>
<p>&nbsp;public DragPanel() {<br />
&nbsp;&nbsp;this.setLayout(new BorderLayout());<br />
&nbsp;&nbsp;north_JP = new JPanel();<br />
&nbsp;&nbsp;north_JP.add(new JButton("1"));<br />
&nbsp;&nbsp;north_JP.add(new JButton("2"));<br />
&nbsp;&nbsp;north_JP.add(new JButton("3"));<br />
&nbsp;&nbsp;north_JP.add(new JButton("4"));<br />
&nbsp;&nbsp;this.add("North", north_JP);<br />
&nbsp;&nbsp;cen_JP = new JScrollPane();<br />
&nbsp;&nbsp;cen_JP.setViewportView(new JTree());<br />
&nbsp;&nbsp;this.add(cen_JP);<br />
&nbsp;&nbsp;north_JP.addMouseListener(this);<br />
&nbsp;&nbsp;north_JP.addMouseMotionListener(this);</p>
<p>&nbsp;}</p>
<p>&nbsp;public void mouseClicked(MouseEvent e) {</p>
<p>&nbsp;}</p>
<p>&nbsp;public void mouseEntered(MouseEvent e) {<br />
&nbsp;&nbsp;// TODO Auto-generated method stub</p>
<p>&nbsp;}</p>
<p>&nbsp;public void mouseExited(MouseEvent e) {<br />
&nbsp;&nbsp;System.out.println("exit");<br />
&nbsp;&nbsp;this.setCursor(Cursor.getDefaultCursor());<br />
&nbsp;}</p>
<p>&nbsp;public void mousePressed(MouseEvent e) {<br />
&nbsp;&nbsp;// TODO Auto-generated method stub</p>
<p>&nbsp;}</p>
<p>&nbsp;public void mouseReleased(MouseEvent e) {<br />
&nbsp;&nbsp;this.setCursor(Cursor.getDefaultCursor());<br />
&nbsp;}</p>
<p>&nbsp;boolean dragFlag = false;</p>
<p>&nbsp;public void mouseDragged(MouseEvent e) {<br />
&nbsp;&nbsp;dragFlag = true;<br />
&nbsp;&nbsp;if (flag) {<br />
&nbsp;&nbsp;&nbsp;this.setCursor(new Cursor(Cursor.N_RESIZE_CURSOR));<br />
&nbsp;&nbsp;&nbsp;Point po = e.getPoint();<br />
&nbsp;&nbsp;&nbsp;Dimension di = north_JP.getPreferredSize();<br />
&nbsp;&nbsp;&nbsp;north_JP.setPreferredSize(new Dimension(di.width,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(north_JP.getY() + po.y)));<br />
&nbsp;&nbsp;&nbsp;north_JP.revalidate();<br />
&nbsp;&nbsp;&nbsp;north_JP.repaint();<br />
&nbsp;&nbsp;}<br />
&nbsp;}</p>
<p>&nbsp;boolean flag = false;</p>
<p>&nbsp;public void mouseMoved(MouseEvent e) {<br />
&nbsp;&nbsp;Point point = e.getPoint();<br />
&nbsp;&nbsp;this.setCursor(Cursor.getDefaultCursor());<br />
&nbsp;&nbsp;flag = false;<br />
&nbsp;&nbsp;dragFlag = false;<br />
&nbsp;&nbsp;if (((north_JP.getY() + north_JP.getHeight() - point.y) &lt; 5)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&amp;&amp; ((north_JP.getY() + north_JP.getHeight() - point.y) &gt; -5)) {<br />
&nbsp;&nbsp;&nbsp;this.setCursor(new Cursor(Cursor.N_RESIZE_CURSOR));<br />
&nbsp;&nbsp;&nbsp;flag = true;<br />
&nbsp;&nbsp;} else {<br />
&nbsp;&nbsp;&nbsp;this.setCursor(Cursor.getDefaultCursor());<br />
&nbsp;&nbsp;}<br />
&nbsp;}</p>
<p>}<br />
<br />
3、载入修改properties的例子<br />
</p>
<p>package proper;</p>
<p>import java.awt.BorderLayout;<br />
import java.awt.FlowLayout;<br />
import java.awt.event.ActionEvent;<br />
import java.awt.event.ActionListener;<br />
import java.io.File;<br />
import java.io.FileInputStream;<br />
import java.io.FileNotFoundException;<br />
import java.io.FileOutputStream;<br />
import java.io.IOException;<br />
import java.util.Iterator;<br />
import java.util.Map;<br />
import java.util.Properties;<br />
import java.util.Vector;<br />
import java.util.Map.Entry;</p>
<p>import javax.swing.JButton;<br />
import javax.swing.JFrame;<br />
import javax.swing.JPanel;<br />
import javax.swing.JScrollPane;<br />
import javax.swing.JTable;<br />
import javax.swing.table.DefaultTableModel;</p>
<p>/**<br />
&nbsp;* <br />
&nbsp;* Test Class<br />
&nbsp;* <br />
&nbsp;* author:zht<br />
&nbsp;* mail:zht_dream@hotmail.com<br />
&nbsp;* 2007-10-31<br />
&nbsp;*/<br />
public class Test extends JPanel implements ActionListener {</p>
<p>&nbsp;/**<br />
&nbsp; * @param args<br />
&nbsp; */<br />
&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;// TODO Auto-generated method stub<br />
&nbsp;&nbsp;Test test = new Test();<br />
&nbsp;&nbsp;JFrame jf = new JFrame();<br />
&nbsp;&nbsp;jf.setContentPane(test);<br />
&nbsp;&nbsp;jf.pack();<br />
&nbsp;&nbsp;jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br />
&nbsp;&nbsp;jf.setVisible(true);<br />
&nbsp;}</p>
<p>&nbsp;JTable jt;</p>
<p>&nbsp;JButton modify_JBn;</p>
<p>&nbsp;Properties props = null;</p>
<p>&nbsp;public Test() {<br />
&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;props = loadPro();<br />
&nbsp;&nbsp;} catch (FileNotFoundException e) {<br />
&nbsp;&nbsp;&nbsp;// TODO Auto-generated catch block<br />
&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;} catch (IOException e) {<br />
&nbsp;&nbsp;&nbsp;// TODO Auto-generated catch block<br />
&nbsp;&nbsp;&nbsp;e.printStackTrace();<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;this.setLayout(new BorderLayout());<br />
&nbsp;&nbsp;String[] tn = { "名称", " 值", };<br />
&nbsp;&nbsp;String[][] data = {};<br />
&nbsp;&nbsp;DefaultTableModel defaultModel = new DefaultTableModel(data, tn) {<br />
&nbsp;&nbsp;&nbsp;@Override<br />
&nbsp;&nbsp;&nbsp;public boolean isCellEditable(int row, int column) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;if (column == 0) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;} else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;};<br />
&nbsp;&nbsp;jt = new JTable(defaultModel);<br />
&nbsp;&nbsp;this.add(new JScrollPane(jt));<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;Iterator itor = props.entrySet().iterator();<br />
&nbsp;&nbsp;&nbsp;while (itor.hasNext()) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;Map.Entry vEntry = (Entry) itor.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;Vector&lt;Object&gt; vv = new Vector&lt;Object&gt;();<br />
&nbsp;&nbsp;&nbsp;&nbsp;vv.add(vEntry.getKey());<br />
&nbsp;&nbsp;&nbsp;&nbsp;vv.add(vEntry.getValue());<br />
&nbsp;&nbsp;&nbsp;&nbsp;//做一些其他的事&#8230;&#8230;<br />
&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(vEntry.getKey());<br />
&nbsp;&nbsp;&nbsp;&nbsp;defaultModel.addRow(vv);<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;modify_JBn = new JButton("modify");<br />
&nbsp;&nbsp;modify_JBn.addActionListener(this);<br />
&nbsp;&nbsp;JPanel jp = new JPanel();<br />
&nbsp;&nbsp;jp.setLayout(new FlowLayout());<br />
&nbsp;&nbsp;jp.add(modify_JBn);<br />
&nbsp;&nbsp;this.add("South", jp);<br />
&nbsp;}</p>
<p>&nbsp;public Properties loadPro() throws FileNotFoundException, IOException {<br />
&nbsp;&nbsp;Properties props = new Properties();<br />
&nbsp;&nbsp;props.load(new FileInputStream(new File("config/config.properties")));<br />
&nbsp;&nbsp;return props;<br />
&nbsp;}</p>
<p>&nbsp;@Override<br />
&nbsp;public void actionPerformed(ActionEvent e) {<br />
&nbsp;&nbsp;for (int i = 0; i &lt; jt.getRowCount(); i++) {<br />
&nbsp;&nbsp;&nbsp;String name = jt.getValueAt(i, 0).toString();<br />
&nbsp;&nbsp;&nbsp;String value = jt.getValueAt(i, 1).toString();<br />
&nbsp;&nbsp;&nbsp;props.setProperty(name, value);<br />
&nbsp;&nbsp;&nbsp;System.out.println("==");<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;FileOutputStream fos;<br />
&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;fos = new FileOutputStream(new File("config/config.properties"));<br />
&nbsp;&nbsp;&nbsp;props.store(fos, "");<br />
&nbsp;&nbsp;} catch (Exception e1) {<br />
&nbsp;&nbsp;&nbsp;// TODO Auto-generated catch block<br />
&nbsp;&nbsp;&nbsp;e1.printStackTrace();<br />
&nbsp;&nbsp;}<br />
&nbsp;}<br />
}<br />
</p>
</span>
<img src ="http://www.blogjava.net/Swing/aggbug/145073.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Swing/" target="_blank">zht</a> 2007-09-14 10:49 <a href="http://www.blogjava.net/Swing/archive/2007/09/14/145073.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SwingUtilities中invokeLater和invokeAndWait的介绍</title><link>http://www.blogjava.net/Swing/archive/2007/07/10/129338.html</link><dc:creator>zht</dc:creator><author>zht</author><pubDate>Tue, 10 Jul 2007 06:28:00 GMT</pubDate><guid>http://www.blogjava.net/Swing/archive/2007/07/10/129338.html</guid><wfw:comment>http://www.blogjava.net/Swing/comments/129338.html</wfw:comment><comments>http://www.blogjava.net/Swing/archive/2007/07/10/129338.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Swing/comments/commentRss/129338.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Swing/services/trackbacks/129338.html</trackback:ping><description><![CDATA[<p style="COLOR: #008000">SwingUtilities中invokeLater和invokeAndWait介绍<br>&nbsp; &nbsp;在Java中Swing是线程不安全的，是单线程的设计，这样的造成结果就是：只能从事件派发线程访问将要在屏幕上绘制的Swing组件。事件派发线程是调用paint和update等回调方法的线程，它还是事件监听器接口中定义的事件处理方法，例如，ActionListener中的actionPerformed方法在事件派发线程中调用。<br>&nbsp; &nbsp;Swing是事件驱动的，所以在回调函数中更新可见的GUI是很自然的事情，比如，有一个按钮被按下，项目列表需要更新时，则通常在与该按钮相关联的事件监听器的actionPerformed方法中来实现该列表的更新，从事件派发线程以外的线程中更新Swing组件是不正常的。<br>&nbsp; &nbsp;有时需要从事件派发线程以外的线程中更新Swing组件，例如，在actionPerformed中有很费时的操作，需要很长时间才能返回，按钮激活后需要很长时间才能看到更新的列表，按钮会长时间保持按下的状态只到actionPerformed返回，一般说来耗时的操作不应该在事件处理方法中执行，因为事件处理返回之前，其他事件是不能触发的，界面类似于卡住的状况，所以在独立的线程上执行比较耗时的操作可能更好，这会立即更新用户界面和释放事件派发线程去派发其他的事件。<br>&nbsp; &nbsp;SwingUtilities类提供了两个方法：invokeLate和invoteAndWait，它们都使事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时，就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。<br>&nbsp; &nbsp;只有从事件派发线程才能更新组件。<br>&nbsp; &nbsp;程序示例：更新组件的错误方法<br>&nbsp; &nbsp;startButton.addActionListener(new ActionListener() {<br>&nbsp;&nbsp;&nbsp;public void actionPerformed(ActionEvent e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;GetInfoThread t = new GetInfoThread(Test.this);<br>&nbsp;&nbsp;&nbsp;&nbsp;t.start();<br>&nbsp;&nbsp;&nbsp;&nbsp;startButton.setEnabled(false);<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;});<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;class GetInfoThread extends Thread {<br>&nbsp;Test applet;</p>
<p style="COLOR: #008000">&nbsp;public GetInfoThread(Test applet) {<br>&nbsp;&nbsp;this.applet = applet;<br>&nbsp;}</p>
<p style="COLOR: #008000">&nbsp;&nbsp;public void run() {<br>&nbsp;&nbsp;&nbsp;while (true) {<br>&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(500);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;applet.getProgressBar().setValue(Math.random() * 100);<br>&nbsp;&nbsp;&nbsp;&nbsp;} catch (InterruptedException e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>&nbsp;}<br>&nbsp;错误分析：在actionPerformed中，监听器把按钮的允许状态设置为false，由于是在事件派发线程上调用actionPerformed，所以setEnabled是一个有效的操作，但是在GetInfoThread中设置进度条是一个危险的做法，因为事件派发线程以外的线程更新了进度条，所以运行是不正常的。</p>
<p style="COLOR: #008000">&nbsp; &nbsp;1、invokeLater使用<br>&nbsp; &nbsp; class GetInfoThread extends Thread {<br>&nbsp;&nbsp;Test applet;<br>&nbsp;<br>&nbsp;&nbsp;Runnable runx;<br>&nbsp;<br>&nbsp;&nbsp;int value;</p>
<p style="COLOR: #008000">&nbsp;&nbsp;public GetInfoThread(final Test applet) {<br>&nbsp;&nbsp;&nbsp;this.applet = applet;<br>&nbsp;&nbsp;&nbsp;runx = new Runnable() {<br>&nbsp;&nbsp;&nbsp;&nbsp;public void run() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JProgressBar jpb = applet.getProgressBar();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jpb.setValue(value);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;}</p>
<p style="COLOR: #008000">&nbsp;&nbsp;&nbsp;public void run() {<br>&nbsp;&nbsp;&nbsp;&nbsp;while (true) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(500);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value = (int) (Math.random() * 100);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(value);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;