技术改变世界

 

Swing第二刀:枝间新绿一重重

关于绿色

        喜欢绿色,喜欢雅黑,无可救药。在这个吵吵闹闹的软件行业,绿色也忽然从“春风一拂千山绿”唯美变成俗不可耐的buzzword。比如:

        绿色软件:大大的buzzword。忽然一夜之间,所有的软件都绿色了,好像不“绿”就跟不上形势。比如绿色杀毒,绿色OFFICE,绿色ERP,绿色windows。反正全绿。

        绿色征途:看,精神鸦~片也可以很“绿”的;

        绿~坝-花季护航:呃,老好的软件,不多说了;

        绿色世博:嗯,喊的老响了。至今一头雾水。

        绿色还有一些不好的词,比如人人避之不及的“绿帽子”之类。台湾的绿营也代表了大坏蛋那帮人(至少陈水扁带了个头)。还有“我把老板气的脸都绿了”、”老板整天灯红酒绿“,也都不是什么好词。
        不过绿色更多的还是代表了“春天、自然、环保、低碳”等没好的东西。皮尤慈善信托基金(Pew)在一份名为《绿色改变世界》的研究报告中指出,计算机屏幕如果使用绿色可以节省大量电能并降低辐射,保护使用者双眼和皮肤。例如一个全屏显示的绿色软件界面可以让一台液晶显示器消耗功率降低13.7瓦;假设美国电脑拥有量为5299万台来计算,每年仅关机状态功耗一项可以节约 6.2亿度电。如果我们把所有的软件都设计成“绿色”这个环保色,每台计算机都会减少5%的电能和辐射,人的情绪也会更加平缓舒畅,心脏和双肺由此减少12%的血液循环负担,从而降低人类对氧气的需求和二氧化碳的呼出达到14%。加上节省下来的对皮肤护理保养、心肺疾病治疗、近视以及眼镜相关行业对自然资源的消耗,每年全球可以节省1400亿美元的资源消耗,相当于减少砍伐8500平方公里的亚马逊热带雨林,对于处于正在复苏之中的世界经济具有说一不二的作用。看来,“绿色软件”,先把自己的界面搞“绿”了,就是地球的一大幸事!
        (注意:上述报告和数据纯属胡诌八扯,如有雷同,纯属巧合。)
        但是制作一个绿色的软件界面确实一个心愿。尤其是能够体现“枝间新绿一重重,小蕾深藏数点红”的那种感觉!现在终于有了,经过一个多星期的折腾,终于有了一个雏形,在上一篇博文《Swing是一把刀》中给大家看到的:




框架,还是框架


        这个程序的设计初衷是快速建立一个美观的Swing应用程序外观。但是,具体来说,它又并非完全是下面几个东西:

        LookAndFeel:这个程序并非一个LnF。一个LnF会对所有的Swing组件进行重新定义Paint并可以通过UIManager.setLookAndFeel进行启用。这个程序用到了大量LookAndFeel的机制,甚至也直接定义了不少UI。不过它并不是一个完整的LookAndFeel。这些定制完全是为这个程序框架服务的。也就是说,这些UI和重绘机制只有在当前的程序框架起作用,而无法指望一句UIManager.setLookAndFeel就将你的任意Swing程序变成上图风格。

        组件库:也不是组件库。其实里面的组件,除了这个OutlookPane(左侧的模块树)是完全新做出来的(而且没有从JTabbedPane继承,也许理论上还经不起太严谨的推敲),其他的组件都是很简单、现成的。例如列表、按钮、菜单等,都是直接用Swing的,只是重载了一些方法或者定义了UI而已。而且我并非是想让大家直接new OutlookPane()这样来使用,而是使用XML文件对整个界面进行配置使用;

        GUI程序框架:似乎有点大。这仅仅是一个很小的程序而已。

        做Java的喜欢满嘴Framework。你要是不能气定神闲一口气提到20个Framework并有意无意的暗示自己很精通,那~都不好意思跟人家说话;最好再能挑一个有点名气的,指手画脚、评头论足、怒其不争一下下,那就像大牛了(例如Hibernate就是个很不错的candidate)。所以,咱这个小程序也就死乞白赖往“框架”上凑凑,反正已经带上了“绿帽子”,也不怕丢人丢到底。

        好吧,这是一个框架,虽然我也不知道框架该怎么定义。反正我的设计初衷是:如果你用Swing开发一个类似上图结构的应用程序,那么你可以直接用这个框架。这个程序框,包含了上面菜单、地下状态条、左边模块栏、右边功能快捷列表,中间多tab标签页的各种内容(也提供了几个常用的内容页风格,例如列表、流程图等)。这个窗口已经被封装好,通过XML配置文件来定义菜单、状态条、模块栏、流程图、右边的快捷列表。同时,这些都是联动的。例如:点击左边的模块栏中的子模块,一个对应的流程图会显示在中间tab页;选中流程图中的节点,可以把该节点相关的功能列在右侧。点击右侧列表,可以执行各种定义好的动作(动作通过动作码定义,后面会详细介绍)。使用么,直接new 一个窗口类,set各个部分的XML文件名,然后setVisible(true)就OK了。至于中间的各个组件和大家关心的LookAndFeel,则都定义好了,基本上不用太关心细节。

        不管怎么说,我们就叫它“框架”吧。

XML配置

        每个应用程序都千奇百怪,功能各异。如何用一个同样的界面来组织呢?的确,这个界面并非适合所有人。不过这里的所有菜单、按钮、流程图、图标等,其动作都是可以用一个“动作码”类定义的,所有的动作都会回调一个统一的函数。而我们只要在这个函数处插入监听,就可以拦截具体动作码,执行我们想做的任何事情,例如格式化C盘、往aobama@whitehouse.com邮箱发个垃圾邮件啥的。

        例如,要定义主菜单,通过这个XML:

 1   <?  xml version="1.0" encoding="UTF-8"  ?>    
 2
 3    <  menubar  >    
 4
 5      <  menu   text  ="System"   >    
 6
 7        <  menu   text  ="One Sub Module"   >    
 8
 9          <  menuitem   text  ="Test Report Item"   tooltip  ="Tooltip"    icon  ="/free/email.png"   action  ="A001"   />    
10
11          <  menuitem   text  ="Test Report Item"   tooltip  ="Tooltip"   icon  ="/free/email.png"   action  ="A001"   />    
12
13          <  menuitem   text  ="Test Report Item"   tooltip  ="Tooltip"   icon  ="/free/email.png"   action  ="A001"   />    
14
15          <  menuitem   text  ="Test Report Item"   tooltip  ="Tooltip"    icon  ="/free/email.png"   action  ="A001"   />    
16
17    
18
19      </  menu  >    
20
21    </  menubar  >   
22

        以上XML可以定义一个System的主菜单,以及一个One Sub Module的菜单项,以及一系列的二级菜单。每个菜单都可以设置icon图标、文字、tooltip文字,以及动作码(就是那个action)。如下图:



        左侧的模块栏就是典型的Outlook的风格,很多软件干脆都叫它OutlookPane(我这里也是如此)。这个OutlookPane的配置,通过如下类似XML:

<outlook>  

   
<module text="Engineering Box" 

                  icon
="/free/test/module_unselected.png" 

                  selected_icon
="/free/test/module_selected.png" 

                  network
="network.xml">  

   
</module>  

 
</outlook> 
        
        同样,主模块(也就是每个大分栏)包含了模块栏的文字、icon图标(选中和未选中两个),以及一个xml文件。这个xml文件包含了一个流程图,流程图包含了具体的子模块。点击展开大模块栏后,所有的子模块也会显示在栏目中,同时模块的流程关系会通过对应的xml文件中定义的方式,显示在一个图形化的流程图界面中,最终显示在中间的tab页上。


        可以看到,左侧的模块列表和中间的图形节点是一一对应的。当鼠标选中节点后(变成橙色),左侧的列表对应的项也会被选中。同时,和这个节点(代表了一个具体子模块)相关的功能,都会显示在右侧的快捷列表中(这是通过指向的network.xml文件定义的)

        上面例子中的Network.xml内容举例如下:

  1<?xml version="1.0" encoding="UTF-8"?>  
  2
  3 <network>  
  4
  5     <node x="30" y="50" 
  6
  7           text="AP Prepay" 
  8
  9           network_text="AP Prepay" 
 10
 11           tooltip="This is a tooltip of a node" 
 12
 13           icon="/free/test/module.png" 
 14
 15           list_icon="/free/test/submodule.png">  
 16
 17         <button1 tooltip="Tooltip" 
 18
 19                             icon="/free/test/module_attachment.png" action="X002"/>  
 20
 21         <button2 tooltip="Tooltip" 
 22
 23                             icon="/free/test/module_attachment.png" action="X002"/>  
 24
 25         <button3 tooltip="Tooltip" 
 26
 27                             icon="/free/test/module_attachment.png" action="X002"/>  
 28
 29         <shortcuts>  
 30
 31             <separator text="Most Often Used Reports"/>  
 32
 33             <shortcut text="Add a Part" tooltip="Tooltip" 
 34
 35                             icon="/free/submodule.png" action="Z010"/>  
 36
 37             <shortcut text="Delete a Part" tooltip="Tooltip" 
 38
 39                             icon="/free/submodule.png" action="Z010"/>  
 40
 41             <shortcut text="AP Aging Report" tooltip="Tooltip" 
 42
 43                             icon="/free/chart.png" action="Z010"/>  
 44
 45             <shortcut text="MRP for All Parts Used this Month" tooltip="Tooltip" 
 46
 47                             icon="/free/user.png" action="Z010"/>  
 48
 49             <shortcut text="Dashboard of this Month" tooltip="Tooltip" 
 50
 51                             icon="/free/email.png" action="Z010"/>  
 52
 53             <shortcut text="All Open Purchase Orders" tooltip="Tooltip" 
 54
 55                             icon="/free/chart.png" action="Z010"/>  
 56
 57             <shortcut text="Search in Address Book" tooltip="Tooltip" 
 58
 59                             icon="/free/user.png" action="Z010"/>  
 60
 61             <shortcut text="All Online Users" tooltip="Tooltip" 
 62
 63                             icon="/free/email.png" action="Z010"/>  
 64
 65             <separator text="Common Reports"/>  
 66
 67             <shortcut text="All Open Purchase Orders" tooltip="Tooltip" 
 68
 69                             icon="/free/chart.png" action="Z010"/>  
 70
 71             <shortcut text="Search in Address Book" tooltip="Tooltip" 
 72
 73                             icon="/free/user.png" action="Z010"/>  
 74
 75             <shortcut text="All Online Users" tooltip="Tooltip" 
 76
 77                             icon="/free/email.png" action="Z010"/>  
 78
 79             <shortcut text="All Open Purchase Orders" tooltip="Tooltip" 
 80
 81                             icon="/free/chart.png" action="Z010"/>  
 82
 83             <shortcut text="Search in Address Book" tooltip="Tooltip" 
 84
 85                             icon="/free/user.png" action="Z010"/>  
 86
 87             <shortcut text="All Online Users" tooltip="Tooltip" 
 88
 89                             icon="/free/email.png" action="Z010"/>  
 90
 91             <shortcut text="All Open Purchase Orders" tooltip="Tooltip" 
 92
 93                             icon="/free/chart.png" action="Z010"/>  
 94
 95             <shortcut text="Search in Address Book" tooltip="Tooltip" 
 96
 97                             icon="/test/user.png" action="Z010"/>  
 98
 99             <shortcut text="All Online Users" tooltip="Tooltip" 
100
101                             icon="/free/email.png" action="Z010"/>  
102
103         </shortcuts>  
104
105     </node>  
106
107 </network> 
108

        其中,每个node定义了一个子模块节点。节点上包含x、y坐标信息、文本信息、tooltip、icon图标(中间大的主图标),以及三个按钮。每个node如图所示,可以携带3个按钮。每个按钮可以挂一个图标、tooltip、icon以及动作码。我们可以定义其任意动作。


         
        然后,每个node又携带了一个shortcuts列表,包含了这个节点所有相关的功能点,在node被选中后,以右侧的列表方式列出。如图所示:



        此外,流程图中的箭头是通过类似如下XML在network.xml中定义:

<arrow x="170" y="80" direction="left" rotation="0"/>

        其中x、y是坐标,direction是方向,可以是上下左右以及斜向共8个方向。此外rotation还提供了旋转角度。如果right_up这个45度的右上角度不符合要求,可以在增加rotation进行进一步调节。

        右侧快捷列表的分割文字,也是通过如下xml进行定义:

<shortcuts>  

             
<separator text="Most Often Used Reports"/>  

             
<shortcut text="Add a Part" tooltip="Tooltip" 

                             icon
="/free/test/submodule.png" action="Z010"/>  

  

        分隔条可以携带一个文字,用来对很多列表项进行分组:


        还有工具条也是可以配置的。工具条在这个框架里被放在了模块栏的顶部。通过如下XML配置其按钮:

 1<?xml version="1.0" encoding="UTF-8"?>  
 2
 3 <toolbar>  
 4
 5     <button tooltip="Tooltip" icon="/free/test/message.png" action="B001" />  
 6
 7     <separator/>  
 8
 9     <button tooltip="Tooltip" icon="/free/test/user.png" action="B001" />  
10
11     <separator/>  
12
13     <button tooltip="Tooltip" icon="/free/test/email.png" action="B001" />  
14
15     <separator/>  
16
17     <button tooltip="Tooltip" icon="/free/test/viewer.png" action="B001" />  
18
19     <separator/>  
20
21     <button tooltip="Tooltip" icon="/free/test/chart.png" action="B001" />  
22
23     <separator/>  
24
25     <button tooltip="Tooltip" icon="/free/test/capture.png" action="B001" />  
26
27     <separator/>  
28
29     <button tooltip="Tooltip" icon="/free/test/image_edit.png" action="B001" />  
30
31     <separator/>  
32
33 </toolbar> 
34

        其中<separator>定义了一个分割竖线 ,完全美观之用。显示效果如下:



        最后一个配置项是软件右上角的LOGO。每个系统都想有一个地方漂亮的显示咱家的LOGO,那才有成就感!这里通过menubar菜单的XML文件如下配置:

<logo image="/free/test/logo_company.png" tooltip="WPT Power Transmission" />

        可以对图片、tooltip进行定义。显示效果如下:


        这样,一个完整的”纯绿色“应用程序框架就差不多被”配置“出来了。

程序接口

        其实设计初衷是无需使用源代码,直接使用下方提供的jar包即可进行二次开发。因为毕竟通过XML就可以对界面元素进行定义了。如果要把这个框架集成到你的应用程序中,并执行你的具体动作,只需要对free.Shell这个类进行一个函数重载即可。

public void command(String action) {  

         String message 
= "Perform action " + action + ".";  

         
this.lbStatusMessage.setText(message);  

 }
 

        在Shell这个类中,界面上所有的按钮、菜单、列表等被点击后,都会回调这个Shell的command方法,并传回action字符串,也就是我们在XML中定义的动作码。接着你就用if else或者case啥的进行处理动作吧!当然也可以调用addTab方法在Shell界面中添加一个tab页面。其使用方法会下次详细阐述。

关于效率、内存、布局和其他

        有朋友说很担心执行效率、内存占用。这是对Swing常被攻击的的一个老话题了。简单直接的回答是:Swing效率没问题。内存占用没问题。满眼哗哗一片字符串,就一定占内存吗?table有1000行,渲染的花里胡哨,就内存问题吗?完全不是这么一回事。例如,如果你了解Swing的table的renderer机制,其实无论表格有多少行,一个列是用同一个renderer实例来paint的。注意,是一个实例哈。editor也是一样。很多初学者以为这一列用JComboBox编辑,1000行就会create成1000个ComboBox。那就完全错了。Renderer就是一个“橡皮章”,一个章,不停的在每个格子里面“盖章”,重绘就重新盖;Editor就是当一个“萝卜”,哪个“坑”需要编辑时,table动态把它“放”在这个单元格(坑)上面,下次下个单元格需要编辑,再被挪过去。对于一个列来说,就一个萝卜,一个橡皮章。怎么会说“占用内存”?如果说1000行字符串占用内存,那这些字符串用什么语言和平台不占用内存呢?同样字符串在不同的语言和平台上可以说占用内存几乎没什么差别。这个以后可以专门讨论。

        在这个本程序中,状态栏上有一个封装好的内存监控工具条:


        它的作用是监测目前Java的总的申请的堆内存以及使用的内存。相关代码如下:

MemoryMXBean memorymbean = ManagementFactory.getMemoryMXBean();  

    

 
long usedMemory = memorymbean.getHeapMemoryUsage().getUsed();  

 
long totalMemory = memorymbean.getHeapMemoryUsage().getMax(); 

        上面图片中的“18M/63M”标明默认Java堆大小是64M目前使用了18M。尝试多打开一些表格界面、拓扑图界面,可见内存使用并不是很多。

        说Swing的效率低,也不是很有说服力。其实Swing本质还是Java2D在paint东西,看看那些杂乱的Metal***UI源码就知道了。不过Swing确实设计的够复杂啰嗦,一个LnF就能绕死人。这些机制会导致Swing慢一些。不过说到底程序的快慢瓶颈还不是在Swing上,还是在如何设计和使用上。例如线程的处理等等,这些都不是初学者很容易搞定的东西。这也导致Swing总是被贴上“慢”的标签。

        自动布局方面,也是比较啰嗦的。如果用IDE的话,推荐NetBeans里面的GUI编辑工具,它使用的实际是Matisse这个layout。这里有两篇官方文章介绍:
         http://wiki.netbeans.org/UsingGUIEditor
         http://netbeans.org/features/java/swing.html

        如果手写代码,我还是推荐一个超强但比较复杂的TableLayout。这里有其介绍:
         http://java.sun.com/products/jfc/tsc/articles/tablelayout/

        其他简单的布局,大多可以用Swing内置的几个layout搞定。另外尽量多套用Panel进行嵌套,化繁为简。不要试图一次一个Panel+Layout把一个复杂的界面搞定,那样会很累。

        有朋友很不齿这里使用到了JGoodies和TWaver,这里也说明一下。在JGoodies的基础上再去定制UI确实美观了不少,不过理论上确实可以彻底抛开JGoodies。这样就要完全彻底重写一整套的LnF了,包括文本框按钮啊所有的东西。目前确实还没有这样的时间和精力,以后会考虑出一个WithoutJGoodies版本。使用TWaver主要是为了做中间的流程图和拓扑图,并且使用的也比较顺手了。如果不需要中间的拓扑图流程图,也可以without TWaver,以后会考虑改一版。不过这些东西都是很不错的工具,能用就拿来为我所用,不喜欢用就借鉴一下了事。


程序下载

        麻烦请至我之前发表文章的TWaver中文社区下载。

        执行方法:解压zip文件,双击其中的run.bat。

        运行环境:Java 6。

        其他说明:zip包中的free.jar就是本文提到的框架。其他两个jar包是jgoodies lnf和twaver做流程图的支撑包,可不用理会。

        下一次我会详细介绍每一个组件的制作方法和相关代码。

posted on 2010-08-04 14:49 老三 阅读(2618) 评论(0)  编辑  收藏

导航

公告

一个不错的UI技术中文社区:http://twaver.servasoft.com/
还有一个论坛:http://twaver.servasoft.com/forum/

常用链接

随笔档案

最新随笔

搜索

最新评论