当柳上原的风吹向天际的时候...

真正的快乐来源于创造

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  368 Posts :: 1 Stories :: 201 Comments :: 0 Trackbacks
传统的Swing工具栏的按钮从生成到响应总是需要一堆相似的代码来完成的,如下:
生成工具栏按钮的代码示例:
.....
reopenBtn=new JButton(ResourceUtil.ToolbarMain_Reopen_ImageIcon);
reopenBtn.setToolTipText(
"刷新数据库内容");
toolbar.add(reopenBtn);
// toolbar是工具栏,JToolBar的示例


响应上面这个按钮的点击事件的代码如下:
view.getToolbarPanel().getReopenBtn().addActionListener(new ActionListener() {
    
public void actionPerformed(ActionEvent e) {
        .. 
// 具体处理略
    }
});


如果只是这点代码,程序整体确实没有什么问题,但数量多了就不得了,试想想看,按照MVC的放置原则,生成的代码和响应点击的代码应该放在两个类中,如果需要修改就是两个修改点;而工具栏按钮一般是较多的,假设有十个按钮那么就意味着十对修改点,如果数量再多呢?这对软件的可维护性可谓一个灾难,也为系统的隐患埋下了伏笔。有人说这就要考验维护者对系统的理解和对代码的责任心了,对这种转移问题和把维护人员等同于机器的观点作者不敢苟同,框架的设计者不能把自己应该尽到的责任推卸给维护者。

如果有效利用XML和反射等手段,我们可以做到工具栏菜单项的可配置化。具体来说就是,将菜单项的文字,图片和点击后的响应函数都在XML配置文件中配置好,程序启动时去读取文件生成菜单,点击菜单项后会动态的通过反射找到具体需要处理的函数。这样做以后,修改一个按钮对应的功能定位代码,或是增删一个按钮及其功能就很容易了。下面来看具体的做法:

1.工具栏菜单的配置文件
   <items>    
        
            
        
<item>
            
<icon>tollbar_sqlwindow/run.gif</icon>
            
<function>run</function>
            
<description>执行所选择的Sql语句</description>
        
</item>
        
        
<item>
            
<icon>tollbar_sqlwindow/batchRun.gif</icon>
            
<function>batchRun</function>
            
<description>批量执行所选择的Sql语句,以分号为分割单位</description>
        
</item>
        
        
<item>
            
<icon>tollbar_sqlwindow/format.gif</icon>
            
<function>format</function>
            
<description>格式化所选择的Sql语句</description>
        
</item>
        
        
    
</items>
 
以上子项里,icon是工具栏按钮的对应图片,function是点击按钮后响应的具体函数名,description是用于ToolTipText的文字,这些信息都会被程序读取出来。

2.读取配置文件中的信息
    List<ToolbarItem> items=new ArrayList<ToolbarItem>();
    
    
// 下面开始读取sqlWndToolbar.xml得到菜单项
    try {
        SAXReader reader 
= new SAXReader();
        InputStream is
=TreeMenuPanel.class.getResourceAsStream(“sqlWndToolbar.xml”);// toolbar.xml是具体文件名
        
        Document document 
= reader.read(is);
        Element rootElm 
= document.getRootElement();
        
        List
<Element> elms = rootElm.elements("item");
        
for (Element elm : elms) {
            String icon
=elm.elementText("icon");
            String function
=elm.elementText("function");
            String description
=elm.elementText("description");
            
            ToolbarItem btn
=new ToolbarItem(icon,function,description);
            items.add(btn);
        }    
    } 
catch (Exception ex) {
        DlgUtil.popupErrorDialog(
"无法读取文件"+ResourceUtil.SqlWndToolbar_XMLFile);
        ex.printStackTrace();
    }
    上述代码中,ToolbarItem是包括icon,function,description 三个字符串子项的JavaBean。
    
3.根据读出的信息生成按钮项并添加到工具栏。
   
    for (ToolbarItem item : ToolbarItemLoader.getItems()) {
        toolbar.add(
new ToolbarButton(item.getIcon(), item.getFunction(),
                item.getDescription()));
    }

下面就是添加完成的工具栏:


    ToolbarItemLoader.getItems()会得到上面第二段代码中的items的引用,然后遍历得到子项后就可以生成按钮了。但注意一下,生成的按钮是自定义的ToolbarButton类实例而不是JButton的子类实例,ToolbarButton继承自JButton但多了一个function属性,这个属性对于事件响应是必不可少的,请看下面的代码:
    
4.添加工具栏菜单按钮的事件响应:
for (Component c : toolbar.getComponents()) {
    
if (c instanceof ToolbarButton) {
        
final ToolbarButton btn = (ToolbarButton) c;

        btn.addActionListener(
new ActionListener() {
            
public void actionPerformed(ActionEvent e) {
                
// 要执行的函数
                String function = btn.getFunction();

                
// 所选择的文本
                String selectedText = inputTxt.getSelectedText();

                
// 执行函数
                executeFunction(function, selectedText);
            }
        });
    }
}

上面代码就显示了ToolbarButton的function属性,它作为一个函数的参数传了出去,这个函数用反射来具体定位函数,具体来说就是,function就是要真正调用的函数名。

5.用反射调用具体的函数executeFunction.
private void executeFunction(String fucntionName, String selectedText) {
    
// 利用反射去找对应函数
    try {
        
// 得到实例对应的类
        Class<?> cls = this.getClass();

        
// 通过反射得到方法
        Method method = cls.getMethod(fucntionName,
                
new Class[] { String.class });

        
// 通过反射调用对象的方法
        method.invoke(thisnew Object[] { selectedText });
    } 
catch (NoSuchMethodException e) {
        
// 找不到方法时

        DlgUtil.popupErrorDialog(
"找不到方法" + fucntionName + ".");
    } 
catch (IllegalAccessException e) {
        
// 当访问权限不够时

        DlgUtil.popupErrorDialog(
"方法" + fucntionName + "的访问权限不够.");
    } 
catch (InvocationTargetException e) {
        
// 当调用的函数抛出异常时
        Exception tragetException = (Exception) e.getTargetException();

        StringBuilder sb 
= new StringBuilder();
        sb.append(
"调用函数" + fucntionName + "出现异常,");
        sb.append(
"具体的异常信息为" + tragetException.getMessage() + ".");

        SqlTextDialog dlg 
= new SqlTextDialog("调用函数" + fucntionName
                
+ "出现异常", sb.toString(), 400300);
        dlg.setVisible(
true);
    }
}

通过以上的反射处理后,如果点击的是
<item>
    
<icon>tollbar_sqlwindow/format.gif</icon>
    
<function>format</function>
    
<description>格式化所选择的Sql语句</description>
</item>

这段信息对应的按钮,那么本类的format函数就会得到调用。
public void format(String selectedText) throws Exception {

}

上面需要注意的有几点:
一是被反射调用的函数的访问权限需要公有,否则会发生IllegalAccessException异常;二是函数名一定要和配置文件中的function节点对应好,否则会抛出NoSuchMethodException异常,三是函数有自己的异常抛出后,需要用e.getTargetException()取得其真正异常。

通过以上五步,按钮的生成和响应就可以被一个配置文件所控制,这种方式改善了程序的可维护性,反射是实现改进的五个步骤的核心环节。另外反射也是诸多框架和通用性组件的常用技术之一,值得每个程序员好好掌握。

就是这些,再见吧!

posted on 2010-05-18 22:04 何杨 阅读(2370) 评论(7)  编辑  收藏

Feedback

# re: 使用反射简化Swing工具栏菜单按钮子项的设计 2010-05-19 13:43 BeanSoft
我个人认为如果采用 Netbeans RCP 或者新的 Swing Application Framework 会更好些. 只可惜了 JDK 1.4 下没多少开发框架.  回复  更多评论
  

# re: 使用反射简化Swing工具栏菜单按钮子项的设计 2010-05-19 14:20 何杨
@BeanSoft

建议很好啊,不过现在我还不熟悉它们,只能用熟的来开发了。  回复  更多评论
  

# re: 使用反射简化Swing工具栏菜单按钮子项的设计 2010-05-19 17:25 kunrey
可以看一下spring-richclient 采用的是这种思想,不过不会指定调用方法,这个稍微封装一下应该不难  回复  更多评论
  

# re: 使用反射简化Swing工具栏菜单按钮子项的设计 2010-05-19 17:38 何杨
@kunrey

spring-richclient没有接触过。Spring什么时候弄出个桌面框架的?  回复  更多评论
  

# re: 使用反射简化Swing工具栏菜单按钮子项的设计 2010-05-20 09:58 点对点
@何杨
spring-rich-client框架的思想非常不错,看得使人赏心悦目的  回复  更多评论
  

# re: 使用反射简化Swing工具栏菜单按钮子项的设计 2010-05-20 10:02 何杨
@点对点

谢谢,既然大家都说好,那看来要研究一番了。
  回复  更多评论
  

# re: 使用反射简化Swing工具栏菜单按钮子项的设计 2010-05-21 11:04 何杨
@点对点

spring-rich-client才1.1.0版,再学一个桌面框架耗费挺大的,另外也不清楚它和Spring主体到底是什么关系,如果开发团队力量小导致项目无疾而终就亏大了。我打算不预先抄底,等它成气候再说。  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: