Swing框架之Component:续文三

Swing框架之Component:续文三

Swing事件与事件处理器模型

         Component在Swing模型中是事件触发源。前一篇文章在描述Swing的事件处理模型时就已经提到了这个事件处理过程。简单来说,Swing组 件在侦听到原生事件并处理后,往往产生新的逻辑事件。逻辑事件是某些组件所特有的、具有特定语义的事件,比如JButton按下时产生 ActionEvent、JComboBox一项被选中时产生ItemEvent,等等。和原生事件不同,它们并不被派发到系统事件队列中,而是由组件直 接触发。事件处理器作为组件的观察者添加到组件上并侦听触发的事件。假设事件名叫XXX,Swing中实现这个模式的一般模式是:

1.定义一个XXXEvent

public class XXXEvent extends Event{
    ...
    public void XXXEvent(Object src){
        super(src);
        ...
     }
   
...
}

2.定义一个事件处理器接口XXXListener,声明所有和该事件相关的处理方法:

public interface XXXListener extends EventListener{
    void action1(XXXEvent evt);
    void action2(XXXEvent evt);
    ...
}

3.在触发它的组件中定义一下方法:

public class MyComponent extends Jcomponent{
   
...
   
//存放事件处理器的队列
   
private ArrayList<XXXListener>xxxListeners=new ArrayList<XXXListener>();
       
//定义以下各种方法,访问符号用public,以方便添加删除处理器
       
public void addXXXListener(XXXListener listener){
           
xxxListeners.add(listener);
       
}
       
public void removeXXXListener(XXXListener listener){
           
xxxListeners.remove(listener);
       
}
       
//定义各种触发(fire)action1、action2...的方法,注意一般使用protected,以便继承和扩展
       
//每一个action都要定义一个相应触发(fire)的方法
       
protected void fireAction1(XXXEvent evt){
           
for(XXXListener listener:xxxListeners){
               
listener.action1(evt);
           
}
       
}
       
protected void fireAction2(XXXEvent evt){
           
for(XXXListener listener:xxxListeners){
               
listener.action2(evt);
       
}
   
}
   
...
   
//在某些地方,比如鼠标处理函数中触发相应的动作
   
void myMouseReleased(MouseEvent evt){
       
...
       
if(应该触发action1)
           
fireAction1(new XXXEvent(this));
       
...
       
if(应该触发action2)
           
fireAction2(new XXXEvent(this));
       
...
    
}
}

         XXXEvent、XXXListener、addXXXListener、removeXXXListener以及各种fireAction函数多是重复性代码,有些Java IDE如JBuilder中能够根据开发者的指定参数的自动生成这些代码。

         实际上这个观察者模式的编程范式可以推广到任何JavaBeans,不一定是可视化的Swing组件。以前曾经见过JBuilder做的一个所谓数据库操 作的JavaBeans,它没有界面,但它和Swing组件完全一样添加删除处理器。它的功能是异步操作数据库,在数据操作完了之后触发注册在上面的事件 处理器,该事件处理器就可以将查询结果展现在表格中,或者输出成报表等等。

         在这个模型中,JavaBeans本身既可以是事件源(被观察对象),也可以是事件处理器(观察者),JavaBeans也可以侦听自身的事件并且处理。 比如前面文章所提的MyButton在处理鼠标事件时就是自己侦听自己发出的鼠标事件,自己既是事件源,又是事件处理器,形成自反系统。各种各样的 JavaBeans通过这种机制联系成一张事件网,各种JavaBeans就是这个网上的节点,而它们之间的事件触发与事件处理关系就是这张网络上的线。 当某个节点被外界或自身发出的事件所触发时,行成了事件的传播。这个过程很像网络上节点的振动引起周围周围节点振动的模型。下图示意了这种 JavaBeans之间的事件网:

         例如new JscrollPane(new JtextArea())这个系统,它里面包括两个JScrollBar和一个JTextArea,当鼠标拖动事件触发JScrollBar时, JScrollBar处理了这个鼠标拖动事件,并发出滚动条拖动事件,这个事件传播给JTextArea,JTextArea处理这个拖动事件,相应的更 新自己显示的内容,如果JTextArea之后又根据更新发出了一个新的事件,这个事件便会继续传播下去。

Swing布局管理器

         现在高级图形用户界面工具一般都包括布局管理器机制。什么叫做布局管理器?如果所有窗口的大小是不变的,那么我们在往窗口中添加组件时,只要将组件的拖放 到固定位置、调整好尺寸就可以了,就像VB的界面工具一样。可大多数情况并非如此,用户经常需要调整窗口的大小,以便和其他程序协同工作。这种情况下,在 传统界面工具中,比如VB,就需要显式的侦听窗口尺寸调整事件,根据当前窗口的大小重新计算并调整各个组件的大小和位置。AWT/SWT/Swing将这 个过程自动化、模块化了,抽象出一个布局管理器来负责管理界面组件的布局。

         它们实现原理是相似的:容器类组件侦听初始化、invalide/validate以及容器尺寸调整等事件,一旦发生这些事件,容器类组件检查自己是否配 置了布局管理器,如果没有,则不做任何事情;如果有,则将容器内组件的布局代理给布局管理器,让它来完成容器内组件的重新布局。

         容器管理器对象对实现两类接口:LayoutManager和LayoutManager2,LayoutManager2是LayoutManager的一个扩展,允许组件在添加时指定位置参数。它们的定义和含义如下:

public interface LayoutManager {
   
//添加组件comp,并和name关联起来,name可以作为位置等特殊含义参数来使用
   
void addLayoutComponent(String name, Component comp);
   
//删除组件comp
   
void removeLayoutComponent(Component comp);
   
//根据容器内的当前组件,计算容器parent的最优尺寸。
   
Dimension preferredLayoutSize(Container parent);
   
//根据容器内的当前组件,计算容器parent的最小尺寸。
   
Dimension minimumLayoutSize(Container parent);
   
//重新布局容器parent,这儿是主要布局逻辑所在。
   
void layoutContainer(Container parent);
}
public interface LayoutManager2 extends LayoutManager {
   
//添加组件comp,constraints用作指定如何以及位置的参数,这个函数主要是弥补LayoutManager版的addLayoutComponent表达能力欠缺而添加。
   
void addLayoutComponent(Component comp, Object constraints);
   
//根据容器内的当前组件,计算容器parent的最大尺寸。看来除了最优、最小,某些情况下还是需要知道最大。
   
public Dimension maximumLayoutSize(Container target);
   
//指定水平方向上组件之间的相对对齐方式,0表示和源组件对齐,1表示远离源组件。
   
public float getLayoutAlignmentX(Container target);
   
//指定垂直方向上组件之间的相对对齐方式,0表示和源组件对齐,1表示远离源组件。
   
public float getLayoutAlignmentY(Container target);
   
//invalidate这个布局管理器,有时布局管理器为了计算迅速,可能第一次计算之后就将一些数据给缓冲,但是后容器内的组件数目发生变化,这儿的缓冲值就需要调用这个方法通知更新
   
public void invalidateLayout(Container target);


}

         Swing在java.awt和javax.swing中都分别提供大量的布局管理器,这些布局管理器有简单的如FlowLayout,有复杂的如GridBadLayout。用户还可以自己定义自己的布局管理器,由于篇幅原因,这儿略去例子。

         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,你不妨试一下就知道了。

=====================================

         本想再加一节讲述Swing双缓冲机制,但是想到双缓冲并不是Swing模型的核心概念,没有它并不影响理解Swing的总体模型,因此打算把它作为以后的一篇专门技术文章来写。

         这样Swing模型中的Component部分就算是描述完了,从明天开始,讲述Swing模型中的另外三个重要概念:Model、UI Delegate和Renderer。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值