<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title></title>
    <description></description>
    <link>http://kingzhyh.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>Ajax学习笔录—1</title>
        <author>kingzhyh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://kingzhyh.javaeye.com">kingzhyh</a>&nbsp;
          链接：<a href="http://kingzhyh.javaeye.com/blog/192265" style="color:red;">http://kingzhyh.javaeye.com/blog/192265</a>&nbsp;
          发表时间: 2008年05月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><strong>什么是Ajax?</strong></p>
<p>Asychronized javaScript+XML。什么同步，异步我不懂。不就是个脚本么。</p>
<p>用完之后发现什么叫异步</p>
<p>&nbsp;</p>
<p><strong>为什么要使用Ajax</strong></p>
<p>&ldquo;主要用于加强用户体验&rdquo;，问了几个用过的都这么说。用户体验?让别人用起来不卡咯无非。无非也就是做作局部刷新吧，估计也没什么大花头的东西。</p>
<p><strong>怎么入手</strong></p>
<p>找本入门书看看他怎么操作数据库，然后把数据放到界面咯</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://kingzhyh.javaeye.com/blog/192265#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 12 May 2008 15:43:27 +0800</pubDate>
        <link>http://kingzhyh.javaeye.com/blog/192265</link>
        <guid>http://kingzhyh.javaeye.com/blog/192265</guid>
      </item>
      <item>
        <title>Hashtable&amp;HashMap</title>
        <author>kingzhyh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://kingzhyh.javaeye.com">kingzhyh</a>&nbsp;
          链接：<a href="http://kingzhyh.javaeye.com/blog/190417" style="color:red;">http://kingzhyh.javaeye.com/blog/190417</a>&nbsp;
          发表时间: 2008年05月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Hashtable和HashMap类有三个重要的不同之处。第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的，HashMap是Java 1.2引进的Map接口的一个实现。 <br /> <br />也许最重要的不同是Hashtable的方法是同步的，而HashMap的方法不是。这就意味着，虽然你可以不用采取任何特殊的行为就可以在一个多线程的应用程序中用一个Hashtable，但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静态的synchronizedMap()方法，它创建一个线程安全的Map对象，并把它作为一个封装的对象来返回。这个对象的方法可以让你同步访问潜在的HashMap。这么做的结果就是当你不需要同步时，你不能切断Hashtable中的同步（比如在一个单线程的应用程序中），而且同步增加了很多处理费用。 <br /> <br />第三点不同是，只有HashMap可以让你将空值作为一个表的条目的key或value。HashMap中只有一条记录可以是一个空的key，但任意数量的条目可以是空的value。这就是说，如果在表中没有发现搜索键，或者如果发现了搜索键，但它是一个空的值，那么get()将返回null。如果有必要，用containKey()方法来区别这两种情况。 <br /> <br />一些资料建议，当需要同步时，用Hashtable，反之用HashMap。但是，因为在需要时，HashMap可以被同步，HashMap的功能比Hashtable的功能更多，而且它不是基于一个陈旧的类的，所以有人认为，在各种情况下，HashMap都优先于Hashtable。 <br /> <br />关于Properties <br />有时侯，你可能想用一个hashtable来映射key的字符串到value的字符串。DOS、Windows和Unix中的环境字符串就有一些例子，如key的字符串PATH被映射到value的字符串C:\WINDOWS;C:\WINDOWS\SYSTEM。Hashtables是表示这些的一个简单的方法，但Java提供了另外一种方法。 <br /> <br />Java.util.Properties类是Hashtable的一个子类，设计用于String keys和values。Properties对象的用法同Hashtable的用法相象，但是类增加了两个节省时间的方法，你应该知道。 <br /> <br />Store()方法把一个Properties对象的内容以一种可读的形式保存到一个文件中。Load()方法正好相反，用来读取文件，并设定Properties对象来包含keys和values。 <br /> <br />注意，因为Properties扩展了Hashtable，你可以用超类的put()方法来添加不是String对象的keys和values。这是不可取的。另外，如果你将store()用于一个不包含String对象的Properties对象，store()将失败。作为put()和get()的替代，你应该用setProperty()和getProperty()，它们用String参数。</p>
          <br/>
          <span style="color:red;">
            <a href="http://kingzhyh.javaeye.com/blog/190417#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 06 May 2008 23:24:00 +0800</pubDate>
        <link>http://kingzhyh.javaeye.com/blog/190417</link>
        <guid>http://kingzhyh.javaeye.com/blog/190417</guid>
      </item>
      <item>
        <title>Swing框架之Model之二 </title>
        <author>kingzhyh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://kingzhyh.javaeye.com">kingzhyh</a>&nbsp;
          链接：<a href="http://kingzhyh.javaeye.com/blog/190263" style="color:red;">http://kingzhyh.javaeye.com/blog/190263</a>&nbsp;
          发表时间: 2008年05月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="middleSize" id="articleContent">
<p style="font-weight: bold;"><span style="font-size: x-small;"><span style="font-family: Georgia;">轻量级通知</span></span></p>
<p><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下面Swing中的模型使用轻量级通知，它们是基于 ChangeListener、ChangeEvent接口的：</span></span></p>
<table cellspacing="0" border="1" cellpadding="0">
<tbody>
<tr>
<td>Model</td>
<td>Listener</td>
<td>Event</td>
</tr>
<tr>
<td>BoundedRangeModel</td>
<td>ChangeListener</td>
<td>ChangeEvent</td>
</tr>
<tr>
<td>ButtonModel</td>
<td>ChangeListener</td>
<td>ChangeEvent</td>
</tr>
<tr>
<td>SingleSelectionModel</td>
<td>ChangeListener</td>
<td>ChangeEvent</td>
</tr>
</tbody>
</table>
<p><br /><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ChangeListener接口只有一个通用方法：</span></span></p>
<address><span style="font-size: x-small; color: #006666; font-family: Arial;">public void stateChanged(ChangeEvent e)</span></address>
<p><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ChangeEvent中仅有的状态是事件源，因为所有通知中</span></span><span style="font-size: x-small;"><span style="font-family: Georgia;">的事件源</span></span><span style="font-size: x-small;"><span style="font-family: Georgia;">都是相同的，单独一个事件实例可以用作所有来自该模型的通知。使用此机制的模型支持下面的方法来添加和删除ChangeListeners:</span></span></p>
<address><span style="color: #006666;"><span style="font-size: x-small;">&nbsp;&nbsp; public void addChangeListener(ChangeListenerl)</span><br /><span style="font-size: x-small;">&nbsp;&nbsp; public voidremoveChangeListener(ChangeListenerl)</span></span></address>
<p><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 获知JSlider数据发生变化的代码可以使用如下代码实现：</span></span></p>
<address><span style="color: #006666;"><span style="font-size: x-small;">JSlider slider = new JSlider();</span><br /><span style="font-size: x-small;">BoundedRangeModelmodel =slider.getModel();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><br /><span style="font-size: x-small;">model.addChangeListener(new ChangeListener() {</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public voidstateChanged(ChangeEvent e) {</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // need to query themodel</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // to getupdatedvalue...</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BoundedRangeModel m=</span><span style="font-size: x-small;">(BoundedRangeModel)e.getSource();</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("model changed: "+</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m.getValue());</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br /><span style="font-size: x-small;">});</span></span></address>
<p><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为给不想和分离式模型交互的程序提供方便，一些Swing组件类提供了直接在组件上注册ChangeListener的方法（组件可在组件内部侦听模型的数据变化，并将事件传播给任何注册在组件上的Listener），这些通知的唯一区别是，使用模型注册方式的事件源是该模型实例，而使用组件注册方式的事件源是该组件。</span></span></p>
<p><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因此我们可以将前面的例子简化成：</span></span></p>
<address><span style="color: #006666;"><span style="font-size: x-small;">JSlider slider = newJSlider();</span><br /><span style="font-size: x-small;">slider.addChangeListener(new ChangeListener() {</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp; public void stateChanged(ChangeEvent e) {</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //the source will be</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // the slider this time..</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JSlider s=(JSlider)e.getSource();</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("valuechanged: "+</span><span style="font-size: x-small;">s.getValue());</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp; }</span><br /><span style="font-size: x-small;">});</span></span></address>
<p style="font-weight: bold;"><span style="font-size: x-small;"><span style="font-family: Georgia;">状态化通知</span></span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 支持状态化通知的模型根据它们的目的提供不同的Listener接口和事件对象。下表是这些模型接口和事件对象的类：</span></p>
<table cellspacing="0" border="1" cellpadding="0">
<tbody>
<tr>
<td>Model</td>
<td>Listener</td>
<td>Event</td>
</tr>
<tr>
<td>ListModel</td>
<td>ListDataListener</td>
<td>ListDataEvent</td>
</tr>
<tr>
<td>ListSelectionModel</td>
<td>ListSelectionListener</td>
<td>ListSelectionEvent</td>
</tr>
<tr>
<td>ComboBoxModel</td>
<td>ListDataListener</td>
<td>ListDataEvent</td>
</tr>
<tr>
<td>TreeModel</td>
<td>TreeModelListener</td>
<td>TreeModelEvent</td>
</tr>
<tr>
<td>TreeSelectionModel</td>
<td>TreeSelectionListener</td>
<td>TreeSelectionEvent</td>
</tr>
<tr>
<td>TableModel</td>
<td>TableModelListener</td>
<td>TableModelEvent</td>
</tr>
<tr>
<td>TableColumnModel</td>
<td>TableColumnModelListener</td>
<td>TableColumnModelEvent</td>
</tr>
<tr>
<td>Document</td>
<td>DocumentListener</td>
<td>DocumentEvent</td>
</tr>
<tr>
<td>Document</td>
<td>UndoableEditListener</td>
<td>UndoableEditEvent</td>
</tr>
</tbody>
</table>
<address><br /><span style="font-size: x-small;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Listener</span></span><span style="font-size: x-small;"><span style="font-size: x-small;">除了</span></span><span style="font-size: x-small;"><span style="font-size: x-small;">可以直接查询事件对象来跟踪内容</span></span><span style="font-size: x-small;"><span style="font-size: x-small;">改变</span></span><span style="font-size: x-small;"><span style="font-size: x-small;">外，这些API的作用与轻量级通知相似。比如下面的代码动态的跟踪JList被选中的项：<br /><br /></span></span><span style="color: #006666;"><span style="font-size: x-small;">Stringitems[] = {"One", "Two", "Three");</span><br /><span style="font-size: x-small;">JList list = new JList(items);</span><br /><span style="font-size: x-small;">ListSelectionModel sModel =list.getSelectionModel();</span><br /><span style="font-size: x-small;">sModel.addListSelectionListener</span><span style="font-size: x-small;">(new ListSelectionListener() {</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; publicvoidvalueChanged(ListSelectionEvent e) {</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // get change information directly</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">// fromtheevent instance...</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">if (!e.getValueIsAdjusting()) {</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">System.out.println("selection changed: " +</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">e.getFirstIndex());</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">}</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;</span> <span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">}</span><br /><span style="font-size: x-small;">});</span></span><br /><span style="font-size: x-small;"><br /><span style="font-weight: bold; font-size: x-small;"><span style="font-family: Georgia;">自动视图更新</span></span></span></address>
<p><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 模型没有任何表现它视图的固有知识，相反模型只有关心其状态改变的Listener列表，这种需求对于同个模型多个视图的框架来说是至关重要的。Swing组件负责将合适的模型Listener连接起来，以便于模型改变时能正确地重画出自己。如果你发现模型改变时，组件不能自动更新，说明组件的实现就存在错误。</span></span></span></p>
<p><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-weight: bold; font-family: Georgia;">忽略模型</span></span></span></p>
<p><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 正如前面提到的，大多数组件直接在Component类中提供模型定义的API，以方便组件能不用和模型交互就直接操作，这是相当可行的编程方法，尤其是对于GUI状态模型来说。比如下面的JSlider内部getValue的实现，它将调用代理给模型：</span></span></span></p>
<address><span style="color: #006666;"><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;">public int getValue(){</span></span></span><br /><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return getModel().getValue();</span></span></span><br /><span style="font-size: x-small;"><span style="font-size: x-small;">}</span></span></span></address>
<p><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因此程序完全可以这样写：</span></span></span></p>
<address><span style="color: #006666;"><span style="font-size: x-small;"><span style="font-size: x-small;">JSlider slider = new JSlider();</span></span><br /><span style="font-size: x-small;"><span style="font-size: x-small;">int value=slider.getValue();</span></span></span></address>
<p><span style="font-size: x-small;"><span style="font-size: x-small;"><span style="font-weight: bold; font-family: Georgia;">Swing模型总结</span></span></span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 虽然理解了Swing模型设计是如何工作的，但没有必要在所有Swing编程中都使用模型API。你需要注意考虑应用程序各自的需求，决定哪儿使用模型API能帮你提升代码，且不带来不必要的复杂性。</span></span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我特别推荐在Swing中使用应用数据模型（如JTable和JTree等的模型），因为从长期来看，它们能极大地提高你的应用程序可扩展性和模块化度。</span></span></p>
</div>
          <br/>
          <span style="color:red;">
            <a href="http://kingzhyh.javaeye.com/blog/190263#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 06 May 2008 15:46:28 +0800</pubDate>
        <link>http://kingzhyh.javaeye.com/blog/190263</link>
        <guid>http://kingzhyh.javaeye.com/blog/190263</guid>
      </item>
      <item>
        <title>Swing框架之Model之一 </title>
        <author>kingzhyh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://kingzhyh.javaeye.com">kingzhyh</a>&nbsp;
          链接：<a href="http://kingzhyh.javaeye.com/blog/190262" style="color:red;">http://kingzhyh.javaeye.com/blog/190262</a>&nbsp;
          发表时间: 2008年05月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small;">构建应用程序应该</span><span style="font-size: x-small;">以数据为中心，而不是以用户界面为中心，这是一个良好的编程习惯。为支持这种编程范式，Swing为每种带有逻辑数据或值的组件定义了独立的模型接口，这种分割使程序可以选择向Swing组件中嵌入自己的模型实现。</span></p>
<p><span style="font-size: x-small;"><span style="font-family: Georgia;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下面表格列出Swing中组件及其模型的映射关系：</span></span></span></p>
<table cellspacing="0" border="1" cellpadding="0">
<tbody>
<tr>
<td>组件</td>
<td>Model接口</td>
<td>Model类型</td>
</tr>
<tr>
<td>JButton</td>
<td>ButtonModel</td>
<td>GUI状态</td>
</tr>
<tr>
<td>JToggleButton</td>
<td>ButtonModel</td>
<td>GUI状态/应用数据</td>
</tr>
<tr>
<td>JCheckBox</td>
<td>ButtonModel</td>
<td>GUI状态/应用数据</td>
</tr>
<tr>
<td>JRadioButton</td>
<td>ButtonModel</td>
<td>GUI状态/应用数据</td>
</tr>
<tr>
<td>JMenu</td>
<td>ButtonModel</td>
<td>GUI状态</td>
</tr>
<tr>
<td>JMenuItem</td>
<td>ButtonModel</td>
<td>GUI状态</td>
</tr>
<tr>
<td>JCheckBoxMenuItem</td>
<td>ButtonModel</td>
<td>GUI状态/应用数据</td>
</tr>
<tr>
<td>JRadioButtonMenuItem</td>
<td>ButtonModel</td>
<td>GUI状态/应用数据</td>
</tr>
<tr>
<td>JComboBox</td>
<td>ComboBoxModel</td>
<td>应用数据</td>
</tr>
<tr>
<td>JProgressBar</td>
<td>BoundedRangeModel</td>
<td>GUI状态/应用数据</td>
</tr>
<tr>
<td>JScrollBar</td>
<td>BoundedRangeModel</td>
<td>GUI状态/应用数据</td>
</tr>
<tr>
<td>JSlider</td>
<td>BoundedRangeModel</td>
<td>GUI状态/应用数据</td>
</tr>
<tr>
<td>JTabbedPane</td>
<td>SingleSelectionModel</td>
<td>GUI状态</td>
</tr>
<tr>
<td>JList</td>
<td>ListModel</td>
<td>应用数据</td>
</tr>
<tr>
<td>JList</td>
<td>ListSelectionModel</td>
<td>GUI状态</td>
</tr>
<tr>
<td>JTable</td>
<td>TableModel</td>
<td>应用数据</td>
</tr>
<tr>
<td>JTable</td>
<td>TableColumnModel</td>
<td>GUI状态</td>
</tr>
<tr>
<td>JTree</td>
<td>TreeModel</td>
<td>应用数据</td>
</tr>
<tr>
<td>JTree</td>
<td>TreeSelectionModel</td>
<td>GUI状态</td>
</tr>
<tr>
<td>JEditorPane</td>
<td>Document</td>
<td>应用数据</td>
</tr>
<tr>
<td>JTextPane</td>
<td>Document</td>
<td>应用数据</td>
</tr>
<tr>
<td>JTextArea</td>
<td>Document</td>
<td>应用数据</td>
</tr>
<tr>
<td>JTextField</td>
<td>Document</td>
<td>应用数据</td>
</tr>
<tr>
<td>JPasswordField</td>
<td>Document</td>
<td>应用数据</td>
</tr>
</tbody>
</table>
<p style="font-weight: bold; font-family: Georgia;"><span style="font-size: x-small;">Swing模型分类</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing提供的模型分为两大类：GUI状态模型和应用数据模型。</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GUI状态模型是描述GUI控件可视化状态的接口，如按钮是否按下，或列表中那一项被选中。GUI状态模型通常仅在图形用户界面(GUI)环境中用到。</span><span style="font-size: x-small;">通常来说，虽然</span><span style="font-size: x-small;">编写使用GUI状态模型分离程序，</span><span style="font-size: x-small;">尤其是当多个GUI控件共享状态</span><span style="font-size: x-small;">，或当操作一个控件自动更新另一个的值时</span><span style="font-size: x-small;">比较有用</span><span style="font-size: x-small;">，但GUI状态模型在Swing中并不是必需的，完全可以通过组件顶层方法操作GUI控件的状态，而不必和模型直接交互。</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 应用数据模型是描述具有应用程序含义数据的接口，比如表格中的数据，或列表显示的选项。这些数据模型为Swing提供了一个清晰分割应用程序界面和数据逻辑的强大编程模式。对于以数据为核心的Swing组件，比如JTree和JTable，强烈推荐使用数据模型进行交互。</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当然一些组件的模型根据应用场景的不同其分类介于GUI状态模型和应用数据模型之间，比如JSlider和JProgressBar 的BoundedRangeModel。</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing的可分离模型接口并没有明确界定GUI状态模型和应用数据模型。这儿所以做此说明，目的是让你更好的理解何时以及为何要需要使用分离的模型。</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;"><span style="font-weight: bold;">共享模型定义</span><br /></span></p>
<p><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 值得注意的是，上文中表格中，许多组件的数据抽象相似，只需一个接口而不用过分泛化时，组件可以共享同一模型定义。共享模型定义允许在不同组件之间自动连接。比如，JSlider和JScrollBar都使用BoundedRangeModel接口，因此可以在一个JScrollBar和一个JSlider之间共享同一个BoundedRangeModel实例，这样它们之间的状态就总是同步的。</span></p>
<p><a href="http://blog.photo.sina.com.cn/showpic.html#url=http://static4.photo.sina.com.cn/orignal/4b6047bce026f4d111d73" target="_blank"><img title="http://static4.photo.sina.com.cn/orignal/4b6047bce026f4d111d73" src="http://static4.photo.sina.com.cn/orignal/4b6047bce026f4d111d73" border="1" alt="http://static4.photo.sina.com.cn/orignal/4b6047bce026f4d111d73" /></a></p>
<p style="font-weight: bold;"><span style="font-size: x-small;"><span style="font-family: Georgia;">分离模型编程接口</span></span></p>
<p><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用模型的Swing组件必须提供访问修改模型的set/get方法，即模型必须是该组件的限定性属性。比如，JSlider使用BoundedRangeModel接口作为它的模型定义，因此它必须提供下面方法：</span></span></p>
<address><span style="color: #006666;"><span style="font-family: Arial;">&nbsp;&nbsp; public BoundedRangeModel getModel()</span><br /><span style="font-family: Arial;">&nbsp;&nbsp; publicvoidsetModel(BoundedRangeModelmodel)</span></span></address>
<p><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所有Swing组件有一个共同点：如果你不设置它的模型，组件会在内部创建/安装一个缺省模型。这些缺省模型类的命名习惯是在接口名称之前加上&ldquo;Default&rdquo;，比如JSlider的构造函数中初始化一个DefaultBoundedModel对象。</span></span></p>
<address><span style="font-size: x-small;"><span style="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 />}</span><br /></span></address>
<p><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果程序接着调用setModel()，缺省的模型就被替换了，比如下面例子：</span></p>
<address style="font-family: Arial;"><span style="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);</span><br /></address>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于更复杂的模型（如JTable和JList），Swing还提供一个抽象模型实现，让开发者不需要从头开始创建自己的模型。</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如JList的模型接口是ListModel，Swing同时提供了DefaultListModel和AbstractListModel两个类来协助开发者创建自定义的列表模型。</span></p>
<p style="font-weight: bold; font-family: Georgia;"><span style="font-size: x-small;">模型改变通知</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当数据或者发生变动时，模型必须通知所有相关方（比如视图）。Swing模型使用前面文章所讲述的事件模型来实现这种触发。Swing中有两种方法发送这种通知：</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 发送轻量级通知，表明状态已经改变，需要Listener通过查询模型，发现什么改变了并做出响应。此方法的优点是单独事件实例能用作该模型的所有通知，同时对于需要频繁通知的事件非常有用（比如JScrollBar被拖动时)。</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 发送状态化通知，详细描述模型如何改变。这种方法需要为每个通知创建一个新的事件实例。当通知通过查询模型不能有效地给Listener提供足够的信息时，此方法非常有用。比如当JTable的一列表格数据发生改变时。</span></p>
          <br/>
          <span style="color:red;">
            <a href="http://kingzhyh.javaeye.com/blog/190262#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 06 May 2008 15:45:54 +0800</pubDate>
        <link>http://kingzhyh.javaeye.com/blog/190262</link>
        <guid>http://kingzhyh.javaeye.com/blog/190262</guid>
      </item>
      <item>
        <title>Swing框架之Component：续文三 </title>
        <author>kingzhyh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://kingzhyh.javaeye.com">kingzhyh</a>&nbsp;
          链接：<a href="http://kingzhyh.javaeye.com/blog/190261" style="color:red;">http://kingzhyh.javaeye.com/blog/190261</a>&nbsp;
          发表时间: 2008年05月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p style="MARGIN-BOTTOM: 0cm"><span style="font-size: small; font-family: Georgia;"><strong>Swing事件与事件处理器模型</strong></span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Component在Swing模型中是事件触发源。前一篇文章在描述Swing的事件处理模型时就已经提到了这个事件处理过程。简单来说，Swing组件在侦听到原生事件并处理后，往往产生新的逻辑事件。逻辑事件是某些组件所特有的、具有特定语义的事件，比如JButton按下时产生ActionEvent、JComboBox一项被选中时产生ItemEvent，等等。和原生事件不同，它们并不被派发到系统事件队列中，而是由组件直接触发。事件处理器作为组件的观察者添加到组件上并侦听触发的事件。假设事件名叫XXX，Swing中实现这个模式的一般模式是：</span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">1.定义一个XXXEvent</span></p>
<address style="MARGIN-BOTTOM: 0cm"><span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">public class XXXEvent extends Event{</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp; ...</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">public void XXXEvent(Object src){</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">super(src);</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">...</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: x-small; font-family: Arial;">}</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="font-size: x-small; color: #006666; font-family: Arial;">...<br />}</span></address>
<blockquote dir="ltr" style="MARGIN-RIGHT: 0px"></blockquote>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">2.定义一个事件处理器接口XXXListener，声明所有和该事件相关的处理方法：</span></p>
<address style="MARGIN-BOTTOM: 0cm"><span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">public interface XXXListener extends EventListener{</span><br /></span><span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp; void action1(XXXEvent evt);</span></span> <span style="font-size: x-small; font-family: Arial;"><br /></span><span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">void action2(XXXEvent evt);</span></span> <span style="font-size: x-small; font-family: Arial;"><br /></span><span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="font-size: x-small; color: #006666; font-family: Arial;">...<br />}</span></address>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">3.在触发它的组件中定义一下方法：</span></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>
</blockquote>
<address style="MARGIN-BOTTOM: 0cm"><span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">public class MyComponent extends Jcomponent{<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">...<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">//存放事件处理器的队列<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">private ArrayList&lt;XXXListener&gt;xxxListeners=new ArrayList&lt;XXXListener&gt;();<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">//定义以下各种方法，访问符号用public，以方便添加删除处理器<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">public void addXXXListener(XXXListener listener){<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">xxxListeners.add(listener);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">}<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">public void removeXXXListener(XXXListener listener){<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">xxxListeners.remove(listener);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">}<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">//定义各种触发(fire)action1、action2...的方法，注意一般使用protected，以便继承和扩展<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">//每一个action都要定义一个相应触发(fire)的方法<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">protected void fireAction1(XXXEvent evt){<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">for(XXXListener listener:xxxListeners){<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">listener.action1(evt);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">}<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">}<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">protected void fireAction2(XXXEvent evt){<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">for(XXXListener listener:xxxListeners){<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">listener.action2(evt);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">}<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">}<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">...<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">//在某些地方，比如鼠标处理函数中触发相应的动作<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">void myMouseReleased(MouseEvent evt){<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">...<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">if(应该触发action1)<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">fireAction1(new XXXEvent(this));<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">...<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">if(应该触发action2)<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">fireAction2(new XXXEvent(this));<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span> <span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">...<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-size: x-small; color: #006666; font-family: Arial;">}<br />}</span></address>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XXXEvent、XXXListener、addXXXListener、removeXXXListener以及各种fireAction函数多是重复性代码，有些Java IDE如JBuilder中能够根据开发者的指定参数的自动生成这些代码。</span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 实际上这个观察者模式的编程范式可以推广到任何JavaBeans，不一定是可视化的Swing组件。以前曾经见过JBuilder做的一个所谓数据库操作的JavaBeans，它没有界面，但它和Swing组件完全一样添加删除处理器。它的功能是异步操作数据库，在数据操作完了之后触发注册在上面的事件处理器，该事件处理器就可以将查询结果展现在表格中，或者输出成报表等等。</span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在这个模型中，JavaBeans本身既可以是事件源（被观察对象），也可以是事件处理器（观察者），JavaBeans也可以侦听自身的事件并且处理。比如前面文章所提的MyButton在处理鼠标事件时就是自己侦听自己发出的鼠标事件，自己既是事件源，又是事件处理器，形成自反系统。各种各样的JavaBeans通过这种机制联系成一张事件网，各种JavaBeans就是这个网上的节点，而它们之间的事件触发与事件处理关系就是这张网络上的线。当某个节点被外界或自身发出的事件所触发时，行成了事件的传播。这个过程很像网络上节点的振动引起周围周围节点振动的模型。下图示意了这种JavaBeans之间的事件网：</span></p>
<p style="MARGIN-BOTTOM: 0cm"><a href="http://blog.photo.sina.com.cn/showpic.html#url=http://static13.photo.sina.com.cn/orignal/4b6047bc6bf88e2d650cc" target="_blank"><span style="font-size: x-small; font-family: Georgia;"><img src="http://static13.photo.sina.com.cn/orignal/4b6047bc6bf88e2d650cc" border="0" alt="" /></span></a></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 例如new JscrollPane(new JtextArea())这个系统，它里面包括两个JScrollBar和一个JTextArea，当鼠标拖动事件触发JScrollBar时，JScrollBar处理了这个鼠标拖动事件，并发出滚动条拖动事件，这个事件传播给JTextArea，JTextArea处理这个拖动事件，相应的更新自己显示的内容，如果JTextArea之后又根据更新发出了一个新的事件，这个事件便会继续传播下去。</span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: small; font-family: Georgia;"><strong>Swing布局管理器</strong></span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现在高级图形用户界面工具一般都包括布局管理器机制。什么叫做布局管理器？如果所有窗口的大小是不变的，那么我们在往窗口中添加组件时，只要将组件的拖放到固定位置、调整好尺寸就可以了，就像VB的界面工具一样。可大多数情况并非如此，用户经常需要调整窗口的大小，以便和其他程序协同工作。这种情况下，在传统界面工具中，比如VB，就需要显式的侦听窗口尺寸调整事件，根据当前窗口的大小重新计算并调整各个组件的大小和位置。AWT/SWT/Swing将这个过程自动化、模块化了，抽象出一个布局管理器来负责管理界面组件的布局。</span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 它们实现原理是相似的：容器类组件侦听初始化、invalide/validate以及容器尺寸调整等事件，一旦发生这些事件，容器类组件检查自己是否配置了布局管理器，如果没有，则不做任何事情；如果有，则将容器内组件的布局代理给布局管理器，让它来完成容器内组件的重新布局。</span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 容器管理器对象对实现两类接口：LayoutManager和LayoutManager2，LayoutManager2是LayoutManager的一个扩展，允许组件在添加时指定位置参数。它们的定义和含义如下：</span></p>
<address style="MARGIN-BOTTOM: 0cm"><span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">public interface LayoutManager {<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">//添加组件comp，并和name关联起来，name可以作为位置等特殊含义参数来使用<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">void addLayoutComponent(String name, Component comp);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">//删除组件comp<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">void removeLayoutComponent(Component comp);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">//根据容器内的当前组件，计算容器parent的最优尺寸。<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">Dimension preferredLayoutSize(Container parent);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">//根据容器内的当前组件，计算容器parent的最小尺寸。<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">Dimension minimumLayoutSize(Container parent);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">//重新布局容器parent，这儿是主要布局逻辑所在。<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">void layoutContainer(Container parent);<br />}<br />public interface LayoutManager2 extends LayoutManager {<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">//添加组件comp，constraints用作指定如何以及位置的参数，这个函数主要是弥补LayoutManager版的addLayoutComponent表达能力欠缺而添加。<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">void addLayoutComponent(Component comp, Object constraints);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">//根据容器内的当前组件，计算容器parent的最大尺寸。看来除了最优、最小，某些情况下还是需要知道最大。<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">public Dimension maximumLayoutSize(Container target);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">//指定水平方向上组件之间的相对对齐方式，0表示和源组件对齐，1表示远离源组件。<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">public float getLayoutAlignmentX(Container target);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">//指定垂直方向上组件之间的相对对齐方式，0表示和源组件对齐，1表示远离源组件。<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">public float getLayoutAlignmentY(Container target);<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="color: #006666;"><span style="font-size: x-small; font-family: Georgia;">//invalidate这个布局管理器，有时布局管理器为了计算迅速，可能第一次计算之后就将一些数据给缓冲，但是后容器内的组件数目发生变化，这儿的缓冲值就需要调用这个方法通知更新<br /></span><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;</span></span> <span style="font-size: x-small; font-family: Georgia;"><span style="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></span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing在java.awt和javax.swing中都分别提供大量的布局管理器，这些布局管理器有简单的如FlowLayout，有复杂的如GridBadLayout。用户还可以自己定义自己的布局管理器，由于篇幅原因，这儿略去例子。</span></address>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&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，你不妨试一下就知道了。</span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本想再加一节讲述Swing双缓冲机制，但是想到双缓冲并不是Swing模型的核心概念，没有它并不影响理解Swing的总体模型，因此打算把它作为以后的一篇专门技术文章来写。</span></p>
<p style="MARGIN-BOTTOM: 0cm"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样Swing模型中的Component部分就算是描述完了，从明天开始，讲述Swing模型中的另外三个重要概念：Model、UI Delegate和Renderer。</span></p>
          <br/>
          <span style="color:red;">
            <a href="http://kingzhyh.javaeye.com/blog/190261#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 06 May 2008 15:45:04 +0800</pubDate>
        <link>http://kingzhyh.javaeye.com/blog/190261</link>
        <guid>http://kingzhyh.javaeye.com/blog/190261</guid>
      </item>
      <item>
        <title>Swing框架之Component：续文二</title>
        <author>kingzhyh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://kingzhyh.javaeye.com">kingzhyh</a>&nbsp;
          链接：<a href="http://kingzhyh.javaeye.com/blog/190259" style="color:red;">http://kingzhyh.javaeye.com/blog/190259</a>&nbsp;
          发表时间: 2008年05月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Swing的事件处理过程为：事件调度线程(Event Dispatch Thread)从事件队列(EventQueue)中获取底层系统捕获的原生事件，如鼠标、键盘、焦点、PAINT事件等。接着调用该事件源组件的dispachEvent。该方法过滤出特殊事件后，调用processEvent进行处理。processEvent方法根据事件类型调用注册在这个组件上的相应事件处理器函数。事件处理器函数根据这些事件的特征，判断出用户的期望行为，然后根据期望行为改变组件的状态，然后根据需要刷新组件外观，触发带有特定语义的高级事件。此事件继续传播下去，直至调用应用程序注册在该组件上的处理器函数。下图是这个过程的示意图：</span></p>
<div><a href="http://blog.photo.sina.com.cn/showpic.html#url=http://static11.photo.sina.com.cn/orignal/4b6047bc67899c96b51ea" target="_blank"></a></div>
<div>&nbsp; <a href="http://blog.photo.sina.com.cn/showpic.html#url=http://static11.photo.sina.com.cn/orignal/4b6047bc2ea755cee1cca" target="_blank"><img src="http://static11.photo.sina.com.cn/orignal/4b6047bc2ea755cee1cca" border="0" alt="" /></a></div>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上图所示意的过程简要说就是：</span></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">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.</span></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 事件调度线程在应用程序事件处理函数actionPerformed没有完成之前是不能处理下一个事件的，如果应用程序处理函数是一个时间复杂的任务(比如查询数据库并将结果显示到表格中)，后面包括PAINT事件将在长时间内得不到执行。由于PAINT事件负责将界面更新，所以这就使用户界面失去响应。</span></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 打一个比方，事件处理线程就像进入某城唯一的单行道一样，事件相当于汽车。有种PAINT汽车负责为城市运输非常重要的生活物资。但是有一天，PAINT前面有一辆汽车突然坏掉了，司机下来修车。但是这车太难修，一修就是几天，结果后面的PAINT汽车无法前进，物资无法按时运到城里。市民急了，市长虽然不停的打电话催PAINT公司，但即使PAINT公司多添加几辆车也没用。由于进城的唯一条路被那辆车给占着，所以再多的PAINT车也只能堵在路上。<br /></span></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不了解Swing的这种事件处理模型的人往往将时间复杂的任务放在处理函数中完成，这是造成Swing应用程序速度很慢的原因。用户触发这个动作，用户界面就失去了响应，于是给用户的感觉就是Swing太慢了。其实这个错误是程序员造成的，并不是Swing的过失。</span></p>
<blockquote dir="ltr" style="margin-right: 0px;">
<p style="margin-bottom: 0cm; font-family: Georgia;"><span style="font-size: x-small; color: #006666;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 说点题外话，所有采用这种事件模型的用户界面工具都会产生这种问题，包括SWT、GTK、MFC等流行的用户界面工具。之所以只有Swing被误解，主要是和Swing的历史、市场时机、商业宣传策略和心理学相关的。</span></p>
<p style="margin-bottom: 0cm; font-family: Georgia;"><span style="font-size: x-small; color: #006666;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 首先Swing的历史和市场时机极差。Swing出现早期性能也差、错误也多，而Java程序员脱身于传统图形界面工具，对于Swing这种新的事件处理模型并不太了解，而此时正处于Java第一轮狂热的时期，大家都满怀希望做了大量的Swing应用程序，而这些程序中大量存在这种错误方法。于是市场上涌现了大批的这种程序。自从那个时代，因为这些程序，Swing被贴上了慢的标签。又由于当时的Swing界面也丑，和一般的Windows程序风格炯异，更加深人们的这种印象。这种印象一直持续到现在，像烙印一样深深的刻在人们的脑海里。</span></p>
<p style="margin-bottom: 0cm; font-family: Georgia;"><span style="font-size: x-small; color: #006666;">&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低水平开发者给毁了。</span></p>
<p style="margin-bottom: 0cm; font-family: Georgia;"><span style="font-size: x-small; color: #006666;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 再次, 这和商业宣传策略有关。IBM和Eclipse很懂得市场宣传，人们不是认为Java慢吗，就宣传SWT使用原生组件，人们不是认为Swing丑陋、风格炯异吧，就宣传SWT风格一致性，人们不是认为Java不能做桌面应用吗，就宣传基于SWT的Eclipse。其实这一切的背后原因只是&ldquo;人&rdquo;的不同，Eclipse的开发者和Swing应用程序的开发者，Swing和SWT技术差异并没有造成那么大的差别，如果是相近能力的人使用他们开发的话，应该能做出相近的产品。这可以从现在Eclipse和NetBeans、Intellij IDEA、JDeveloper和JBuilder看的出来。</span></p>
<p style="margin-bottom: 0cm;"><span style="color: #006666;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最后，人类有一个心理学现象，就是一旦形成对某种事物的印象，很难摆脱旧的认识，有时甚至人们不愿意承认摆在眼前的事实。总而言之，Swing和SWT不同遭遇是因为历史、市场时机、商业宣传策略、心理学的种种原因造成的。</span><br /></span></p>
</blockquote>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么如何避免这个问题，编写响应速度快的Swing应用程序呢？在SwingWorker的javadoc中有这样两条原则：</span></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">Time-consuming tasks should not be run on the Event Dispatch Thread. Otherwise the application becomes unresponsive. 耗时任务不要放到事件调度线程上执行，否则程序就会失去响应。</span></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">Swing components should be accessed on the Event Dispatch Thread only. Swing组件只能在事件调度线程上访问。</span></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因此处理耗时任务时，首先要启动一个专门线程，将当前任务交给这个线程处理，而当前处理函数立即返回，继续处理后面未决的事件。这就像前面塞车的例子似的，那个司机只要简单的把车开到路边或者人行道上修理，整个公路系统就会恢复运转。</span></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其次，在为耗时任务启动的线程访问Swing组件时，要使用SwingUtilties. invokeLater或者SwingUtilities.invokeAndWait来访问，invokeLater和invokeAndWait的参数都是一个Runnable对象，这个Runnable对象将被像普通事件处理函数一样在事件调度线程上执行。这两个函数的区别是，invokeLater不阻塞当前任务线程，invokeAndWait阻塞当前线程，直到Runnable对象被执行返回才继续。在前面塞车的例子中，司机在路边修车解决了塞车问题，但是他突然想起来要家里办些事情，这时他就可以打个电话让家里开车来。假如修车不受这件事情的影响，比如叫家人送他朋友一本书，他可以继续修车，这时就相当于invokeLater；假如修车受影响，比如缺少某个汽车零件，叫家人给他送过来，那么在家人来之前，他就没法继续修车，这时就相当于invokeAndWait。</span></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下面举一个例子说明这两点，比如按下查询按钮，查询数据量很大的数据库，并显示在一个表中，这个过程需要给用户一个进度提示，并且能动态显示表格数据动态增加的过程。假设按钮的处理函数是myButton_actionPerformed，则：</span></p>
<address style="margin-bottom: 0cm;"><span style="color: #006666; font-family: Arial;">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 />}</span></address>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JDK1.6以后，Swing提供了一个专门的类SwingWorker能帮你解决这个编程范式，你所需要做的就是继承这个类，重载doInBackground，然后在actionPeformed中调用它的execute方法，并通过publish/process方法来更新界面。SwingWorker的主要方法和它们的作用在下面的示意图：</span></p>
<p style="margin-bottom: 0cm;"><a href="http://blog.photo.sina.com.cn/showpic.html#url=http://static1.photo.sina.com.cn/orignal/4b6047bcb4f1469f89d50" target="_blank"><img src="http://static1.photo.sina.com.cn/orignal/4b6047bcb4f1469f89d50" border="0" alt="" /></a></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&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的版本：</span></p>
<address style="margin-bottom: 0cm;"><span style="color: #006666; font-family: Arial;">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 />}</span></address>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于一般的耗时任务这样做是比较普遍的，但是有一些任务是一旦触发之后，会周期性的触发，如何做处理这种任务呢?JDK中提供了两个Timer类帮你完成定时任务，一个是javax.swing.Timer，一个java.util.Timer。使用它们的方法很简单，对于Swing的timer，使用方法如下：</span></p>
<address style="margin-bottom: 0cm;"><span style="color: #006666; font-family: Arial;">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 />}</span></address>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.util.Timer类似，只不过使用TimerTask完成动作封装。注意这两个Timer有一个关键的区别：Swing的Timer的事件处理都是在事件调度线程上进行的，因而它里面的操作可以直接访问Swing组件。而java.util.Timer则可能在其他线程上，因而访问组件时要使用SwingUtilities.invokeLater和invokeAndWait来进行。这一点要记住。</span></p>
<p style="margin-bottom: 0cm;"><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果要了解更详细的信息，可以查阅SwingWorker、Swing Timer和util Timer这些类javadoc文档和其他网上资料。最重要的是要记住了那两条原则。</span></p>
          <br/>
          <span style="color:red;">
            <a href="http://kingzhyh.javaeye.com/blog/190259#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 06 May 2008 15:43:04 +0800</pubDate>
        <link>http://kingzhyh.javaeye.com/blog/190259</link>
        <guid>http://kingzhyh.javaeye.com/blog/190259</guid>
      </item>
      <item>
        <title>Swing框架之Component：续文一</title>
        <author>kingzhyh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://kingzhyh.javaeye.com">kingzhyh</a>&nbsp;
          链接：<a href="http://kingzhyh.javaeye.com/blog/190256" style="color:red;">http://kingzhyh.javaeye.com/blog/190256</a>&nbsp;
          发表时间: 2008年05月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 昨晚回去后还是觉得Component对象本身说的太简单，想来想去，觉得内容实在是太多，有必要补充两个续文说明Component的其它概念。今天介绍Swing组件paint方法的处理流程，这个流程能使我们理解许多Swing机制。明天续文讲述Swing事件处理器、双缓冲和布局管理器等原理。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&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方法等话语，就是因为这个历史造成的。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上篇文章中的MyButton的paint方法实现是一个非常老式的做法。现在JComponent的实现已经把paint方法改造成可以嵌套多重机制地方，这些机制包括层次渲染、边框、透明背景、双缓冲以及皮肤等。这些机制分别实现不同目的的组件提供了方便。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图形用户界面的组件按照其在组件树上的角色可以分为容器组件和叶组件。Swing模型把叶组件当作是特殊、没有子组件的容器组件，只是JComponent继承Container类，所有Swing组件继承JComponent的原因。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JComponent在paint方法中首先根据组件是否需要使用双缓冲，封装好图形设备对象，然后经过一番处理后调用paintComponent方法画出自身，然后调用paintBorder画出边框，最后调用paintChildren来完成子组件的渲染。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paintComponent意思是画出组件自身，不包括子组件。因此前一文章中的MyButton可以通过覆盖paintComponent方法来完成MyButton的重画。在JComponent实现中，JDK 6的paintComponent的代码为：</span></p>
<address><span style="font-size: x-small; color: #006666; font-family: Arial;">&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; }</span></address>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个方法首先检测组件是否安装了UI Delegate，如果安装了就将渲染过程代理给UI Delegate。这儿是嵌入皮肤的地方。JDK 6中JComponent对应的UI Delegate的update方法缺省的实现是：</span></p>
<address><span style="font-size: x-small; font-family: Arial;"><span style="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 /></span>}</span></address>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以看出，背景透明机制在这儿实现。首先UI Delegate对象判断Component是否背景透明的，如果不是透明的，则使用背景色填充整个Component区域，然后调用paint(g, c)来完成组件在这种LookAndFeel种的渲染。了解了这些后，我们几乎就明白了Swing如何实现背景透明和如何切换皮肤。由于后面的文章还会对UI Delegate和皮肤机制详细描述，这儿就到此为止。</span></p>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 目前还不要求实现皮肤，在这种情况下只需要重载paintComponent方法就行了，如果需要背景透明机制，可以模仿上面代码，MyButton的paintComponent可以这样写：</span></p>
<address><span style="color: #006666; font-family: Arial;">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 />}</span></address>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paintBorder意思是画出组件的边框。Swing所有组件都有边框的概念，就是说可以为任何组件添加各种边框，包括自定义的边框。JDK 6中JComponent的paintBorder的实现是这样的：</span></p>
<address><span style="color: #006666; font-family: Arial;">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 />}</span></address>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 非常直接，如果自己有border，就将画自己边框的任务代理给了这个border，并传给它图形设备和边界参数。Swing缺省提供了大量的各种各样的边框。同样可以定义自己的边框，实现方法就是继承Border类，Border类中有三个方法要实现，它们的含义如下：</span></p>
<address><span style="color: #006666; font-family: Arial;">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 />}</span></address>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这儿实现一个简单的红线边框作为演示：</span></p>
<address><span style="color: #006666; font-family: Arial;">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 />}</span></address>
<p style="font-family: Georgia;"><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; paintChildren完成容器类组件的子组件的渲染。JDK缺省的实现是调用各个自组件的paint方法。一般来说不需要重载这个方法。如果想改变诸如组件Z-order遮挡顺序，可以覆盖这个方法，从相反顺序调用组件的paint方法。</span></p>
<p><span style="font-size: x-small;"><span style="font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 到这儿我们对Swing的结构有了更深化的理解，UI Delegate机制也已经初露倪端。还有几个重要Swing Component概念或者机制没有讲，明天的续文再对它们做出说明。</span></span></p>
          <br/>
          <span style="color:red;">
            <a href="http://kingzhyh.javaeye.com/blog/190256#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 06 May 2008 15:42:05 +0800</pubDate>
        <link>http://kingzhyh.javaeye.com/blog/190256</link>
        <guid>http://kingzhyh.javaeye.com/blog/190256</guid>
      </item>
      <item>
        <title>Swing框架之Component</title>
        <author>kingzhyh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://kingzhyh.javaeye.com">kingzhyh</a>&nbsp;
          链接：<a href="http://kingzhyh.javaeye.com/blog/190255" style="color:red;">http://kingzhyh.javaeye.com/blog/190255</a>&nbsp;
          发表时间: 2008年05月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 昨天晚上写完Swing的模型和渲染器之后，觉得对Swing的体系结构还是没有说清楚。Swing的基础体系结构中的四大基本对象Component、 Model、UI Delegate以及Renderer都值得详细解释。Swing的树状组件结构（虽然这是用户界面工具通有的特征）也值得详细解释，因为这是完成某些复杂Swing组件，尤其像JTable、JTree、JList和JComboBox这种复杂组件中编辑功能得关键。此外，Swing / AWT的事件模型如Event Dispatching和Propagation和事件处理线程模型也需要详细解释，理解这部份是编写高效率Swing界面的关键。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 今天从Swing的四大基本对象Component说起。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&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="font-size: x-small; font-family: Georgia;"><img src="http://static16.photo.sina.com.cn/orignal/4b6047bc7bafae10221ef" alt="" /></span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Component代表Swing对应用程序提供了如下几类编程接口：</span></p>
<ol>
<li><span style="font-size: x-small; font-family: Georgia;">用户界面的组件树的创建和修改的方法。这包括组件的添加和删除等操作。</span> </li>
<li><span style="font-size: x-small; font-family: Georgia;">组件属性访问的方法，比如组件位置、组件前后背景色、组件字体等等。</span> </li>
<li><span style="font-size: x-small; font-family: Georgia;">组件状态及生命周期的管理的方法，比如隐藏和显示、创建和销毁等等。</span> </li>
<li><span style="font-size: x-small; font-family: Georgia;">组件位置、大小的管理，包括通过布局管理器的方法。</span> </li>
<li><span style="font-size: x-small; font-family: Georgia;">组件事件处理接口的管理，包括添加、删除等操作。</span> </li>
</ol>
<p><span style="font-size: x-small; font-family: Georgia;">&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="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于复合型(Composite)组件很简单，这儿要讲述的是如何实现自定义的组件，比如表盘，比如温度计这些没有标准组件可以使用的组件，那么如何自己实现这种自定义组件呢？</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不考虑Model分隔和UI Delegate皮肤分离问题，能够简化自定义Component的模型。总的来说自定义组件需要完成两样基本任务：第一侦听并处理低层事件，根据具体情况改变组件状态，如需要还要发出高级事件；第二，根据当前组件的状态画出当前组件的外观。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 侦听底层的事件是指侦听类似于mouse、keyboard、focus等事件，然后处理此事件，如果发现此事件带有特定语义，表达某种组件行为，则改变当前的组件状态以记录，并触发某种事件通知应用程序进行处理。举例说明，想象你准备实现一个简单的按钮，你可以通过继承JComponent来完成。你可以在按钮初始化时，注册此按钮的鼠标事件侦听器，以侦听发生自己组件上的鼠标事件。当按钮捕获到鼠标按下时，检查鼠标按下的点是否在按钮有效区域内。如果是，则认为当前是一个按钮按下动作，那么改变按钮的状态为按下去，调用repaint方法通知按钮重画成按下去的状态，然后发出 ActionPerformed的事件，通知注册在此按钮上的应用程序的ActionListener处理这个动作。下面是一个简单示意代码：</span></p>
<address><span style="color: #006666;"><span style="font-size: x-small; font-family: Arial;">public class MyButton extends Jcomponent implements MouseListener{</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp; private String text;</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp; private boolean pressed=false;</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp; private ArrayList&lt;ActionListener&gt; listeners=new ArrayList&lt;ActionListener&gt;();</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp; public MyButton(){</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; addMouseListener(this);//将自己注册为自己的鼠标事件侦听器，监听鼠标事件</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp; }</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp; ....</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; public void mousePressed(MouseEvent evt){</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Point p=evt.getPoint();</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(getBounds().contains(p)){//判断鼠标落点是否在有效区域内。</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pressed=true; //鼠标点击的含义是按钮被按下！改表按钮状态。</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; repaint();&nbsp;&nbsp;&nbsp; //通知按钮重画，由原来的抬起状态改变成按下状态。</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fireActionPerformed(new ActionEvent(this)); //这是一个按钮动作事件，触发它。</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; }</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; public void addActionListener(ActionListener listener){</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; listeners.add(listener);</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; }</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; public void removeActionListener(ActionListener listener){</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; listeners.remove(listener);</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; }</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; protected fireActionPerformed(ActionEvent evt){</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(ActionListener listener:listeners){</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; listener.actionPerformed(evt);</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; }</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; ...</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; //这儿你要覆盖paint方法，实现按钮状态的重画</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; public void paint(Graphics g){</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(pressed){</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //画出按下的样子</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }else{</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //画出抬起的样子</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span><br /><span style="font-size: x-small; font-family: Arial;">&nbsp;&nbsp; }</span><br /><span style="font-size: x-small; font-family: Arial;">}</span></span></address>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面要注意的是你要自己管理自定义组件的事件监听器，包括addListener和removeListener方法，以及如何触发。这个过程很简单，基本上就是上面的模板来实现添加删除和触发。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 除了要负责事件的处理和新事件的触发，自定义组件第二个要完成的任务就是要根据组件当前的状态改变重画组件的外观。重画组件的外观只需要覆盖public void paint(Graphics g)方法即可以，在这个方法里，你只需要根据当前的组件状态分别画出当前的组件即可。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当然除了上面两个基本准则外，不要忘了添加访问你的组件属性的方法，比如，如果上面的按钮是个二元按钮（相当于 JCheckbox/JToggleButton的那种按钮），你可能需要提供isPressed或者setPressed来获取和设置当前按钮的状态。注意，在设置状态按钮变化的访问方法中，比如setPressed，你需要使用repaint方法通知按钮重新渲染(复杂的实现可能包括触发propertyChange事件，这儿从简)：</span></p>
<address><span style="font-family: Arial;"><span style="color: #006666;"><span style="font-size: x-small;">public void setPressed(boolean p){</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp; pressed=p;</span><br /><span style="font-size: x-small;">&nbsp;&nbsp;&nbsp; repaint();</span><br /><span style="font-size: x-small;">}</span></span></span></address>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 到此为止，你已经能根据上面的两条准则简单的实现你想要的组件了。但是你发现没有，你的按钮状态和外观行为都被堆到了Component （MyButton)中实现了，而且，对于各个平台都是一个样子，不能换皮肤。这对于比较简单、不想要皮肤的组件，可能没有什么，但是对于复杂的组件，比如JTable或者甚至Excel类似的电子表格的那种组件，你把数据（组件状态)和外观堆在这儿实现就严重违反了MVC原则。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如何简化这种组件的实现呢？使你实现的此种组件容易维护、扩展以及重用，皮肤容易换呢？这就需要Swing结构中的另外三个元素：Model、UI Delegate和Renderer，后面的几个文章将讲述Model、UI Delegate和Renderer帮你逐步实现一个复杂、灵活、可扩展、高效的矢量组件。</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">=====================================================</span></p>
<p><span style="font-size: x-small; font-family: Georgia;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 今天这样讲，不知道讲明白没有。当初刚开始学习Swing的时候，还不了解Swing的这种MVC结构，因此当时自己做的自定义组件都是这样写的，没有单独的Model和可以定制的UI Delegate，更不用说Renderer了。但是我觉得自己的这个学习过程，恰恰是人们学习Swing的最佳途径，先简化模型，然后逐步扩展，直到了解Swing模型的全部图像</span></p>
          <br/>
          <span style="color:red;">
            <a href="http://kingzhyh.javaeye.com/blog/190255#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 06 May 2008 15:41:19 +0800</pubDate>
        <link>http://kingzhyh.javaeye.com/blog/190255</link>
        <guid>http://kingzhyh.javaeye.com/blog/190255</guid>
      </item>
      <item>
        <title>Swing模型与渲染器</title>
        <author>kingzhyh</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://kingzhyh.javaeye.com">kingzhyh</a>&nbsp;
          链接：<a href="http://kingzhyh.javaeye.com/blog/190252" style="color:red;">http://kingzhyh.javaeye.com/blog/190252</a>&nbsp;
          发表时间: 2008年05月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>本文承接Swing/AWT/SWT比较一文，概要叙述Swing的体系结构，解释了Swing架构关键概念：模型与渲染器，解释如何使用渲染对象扩展该体系架构来支持大数据量的组件。后面的文章还会简要概述SWT的体系结构，为Swing/AWT和SWT的比较做一铺垫。 <br /><br /><br />＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝ <br /><br />Java基础类(JFC)Swing工具提供了使用Java平台创建高度可交互性图形用户界面的类。Swing是高度灵活的，但是也因此相当复杂，虽然新手能够使用Swing创建基本的图形用户界面(GUI)，但是真要创建一个复杂、专业的GUI界面，你必须理解Swing的体系架构的基础，尤其是使用 Swing创建复杂、像JTable、JTree、JComboBox以及JList这样基于渲染器的组件，Swing提供的基于模型和渲染器的组件是构建高性能、可扩展GUI的关键。 <br /><br />Swing体系结构 <br /><br />最初Smalltalk系统的UI工具使用所谓的模型－视图－控制(MVC)模式，MVC引入这样一个概念：数据源应该同屏幕展现分开。这是一个优秀的体系设计结构，能促进代码重用和程序框架。Swing使用的是一个变体的MVC架构，如图所示： </p>
<p><a href="http://blog.photo.sina.com.cn/showpic.html#url=http://static10.photo.sina.com.cn/orignal/4b6047bc27d7398808689" target="_blank" style="font-family: Georgia;"><span style="font-size: x-small;"><img title="http://static10.photo.sina.com.cn/orignal/4b6047bc27d7398808689" src="http://static10.photo.sina.com.cn/orignal/4b6047bc27d7398808689" border="1" alt="http://static10.photo.sina.com.cn/orignal/4b6047bc27d7398808689" /></span></a><br /><br /><br /><br />典型的Swing GUI组件包括至少三个对象：一个Component，一个Model和一个UI Delegate，在这个框架中，Model负责存储数据，UI Delegate负责从Model获取数据并渲染到屏幕上去，Component通常协调Model和Delegate之间的操作，并同时负责将 Swing嵌入到AWT窗口系统中。 <br /><br />注意，UI Delegate对象可以在运行的时候动态替换，这就使Swing具备了可插拔的外观(Pluggable Look-And-Feel, PLAF)。 <br /><br />虽然Swing的MVC结构显然具备灵活性的好处，但是这个结构通常被指责为一些程序慢的根源。虽然基于MVC结构需要更多的方法调用来支持额外的重定向，其实花费在这儿的消耗很小。对基于Swing的应用程序profile的结果显示，model-view分隔的开销可以忽略不计，不到CPU总开销的 1%，复杂的Swing用户界面的多数处理事件其实都花费在了底层的图形操作上了。Swing的model-view结构并不是低性能的根源，它是构建可扩展程序的关键。 <br /><br />矢量组件 <br /><a href="http://blog.photo.sina.com.cn/showpic.html#url=http://static16.photo.sina.com.cn/orignal/4b6047bc7bafae10221ef" target="_blank" style="font-family: Georgia;"><span style="font-size: x-small;"><img title="http://static16.photo.sina.com.cn/orignal/4b6047bc7bafae10221ef" src="http://static16.photo.sina.com.cn/orignal/4b6047bc7bafae10221ef" border="1" alt="http://static16.photo.sina.com.cn/orignal/4b6047bc7bafae10221ef" /></span></a><br /><br />Swing提供了一些处理大数据量数据集的组件，包括JTable、JTree、JList以及JComboBox。这些矢量组件被设计成能够处理成千上万甚至数百万的数据，为了避免占用大量内存，这些组件在Swing的体系架构增加了渲染器(renderer)概念。下图是增加了渲染器结构的Swing 体系架构。 <br /><br /><br /><br />渲染器(Renderer) <br /><br />在这些更为复杂的Swing组件中，渲染器是提供可扩展性的关键。我们以JTable作为渲染器的示例。缺省表格中的每一格可能都有一个JLabel，这对于比较小的数据集来说可行，但是对于大数据集就行不通。比如，如果使用这种表格显示1000x1000的数据集，需要的内存可能要1G，即使每个格子都是空的。 <br /><br />如果解决这种扩展性问题？Swing的JTable使用一个组件来画出所有相同类型的格子。比如所有的String对象的格子都使用相同的组件画。这种类型的组件被称作渲染器(renderer)，使用渲染器显示多个表格极大的减小了大型数据表存储空间。 <br /><br />当渲染器用来显示表格时，JTable从model中获取格子中的数据，然后使用这些数据对渲染器进行配置，然后使用该渲染器画出该格子。接下来，渲染器继续移动到下一个格子，然后重复这个过程。 <br /><br /><br />注意你可以通过操作渲染器和模型来控制这个过程，所有的矢量组件，包括JTree、JList以及JComboBox都使用渲染器方法，并不仅限于JTable。 <br /><br />模型(Model) <br /><br />直接操作Swing的模型(Model)对于编写可扩展的用户界面至关重要，下面代码是往JComboBox添加数据项的通常做法： <br /><br />JComboBox box = new JComboBox(); <br />for (int i = 0; i &lt; numItems; i++) { <br />box.addItem(new Integer(i)); <br />} <br />这些代码只是简单的往JComboBox中添加数据项，代码同往AWT的Choice中添加选项类似，这种方法对于小数据量来说可以，但是当要添加大量数据时就会明显变得非常慢。 <br /><br />尽管上面的代码没有明确引用任何模型，JComboBox的模型对象实际上参与这个过程，每次调用addItem时，JComboBox内部发生了许多操作：组件将请求传递给JComboBox的模型，模型发送一个事件表明一个新项被添加。很明显，如果你直接操作模型的将会更高效，如下例所示： <br /><br />Vector v = new Vector(numItems); <br />for (int i = 0; i &lt; numItems; i++) { <br />v.add(new Integer(i)); <br />} <br />ComboBoxModel model = new DefaultComboBoxModel(v); <br />JComboBox box = new JComboBox(model); <br />这样为什么会更快呢？原因有两个。第一，因为所有项是一次添加到模型去，而不是一个一个的，只有一个事件发出，这意味着更少的事件触发，更少的方法调用。第二是因为需要通知变化的对象更少，总的工作量等于触发次数乘以侦听器数目。因为模型是新创建的，侦听在上面的侦听器为零，这意味着没有触发事件发生。 <br /><br />从上面的例子可以学到两点： <br /><br />尽可能使用批操作，尽量减少触发事件的数量。 <br /><br />当初始化或者需要完全替换模型的内容时，考虑重新生成模型，不要使用已经存在的模型，已存在模型上已经保持了很多的侦听器，新生成的模型没有侦听器，这样避免了不必要的处理函数的调用。 <br /><br />触发事件数量严重影响你的程序启动时间，也会影响打开对话框和相似操作的时间。 <br /><br />＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝ <br /><br />本想详细举几个例子进一步说明模型和渲染器的用法和好处，但网络速度还是太慢，写一篇文章太痛苦了...加上篇幅原因，准备以后再写一文，弥补这方面的知识。 <br /><br />以后几篇文章包括（非文章标题）： <br /><br />使用Swing模型和渲染器优化Swing程序。 <br /><br />Swing/AWT事件处理模型以及线程安全。 <br /><br />如何编写响应快速的Swing程序。 <br /><br />SWT设计和实现介绍。 <br /><br />JNI与Java性能的关系。 <br /><br />垃圾收集与虚拟机与Java性能。 <br /><br />Swing与SWT性能比较（前面所有的文章都和这篇有关，是基础）。</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://kingzhyh.javaeye.com/blog/190252#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 06 May 2008 15:34:13 +0800</pubDate>
        <link>http://kingzhyh.javaeye.com/blog/190252</link>
        <guid>http://kingzhyh.javaeye.com/blog/190252</guid>
      </item>
  </channel>
</rss>