Swing事件与事件处理器模型
        ComponentSwing模型中是事件触发源。前一篇文章在描述Swing的事件处理模型时就已经提到了这个事件处理过程。简单来说,Swing组件在侦听到原生事件并处理后,往往产生新的逻辑事件。逻辑事件是某些组件所特有的、具有特定语义的事件,比如JButton按下时产生ActionEventJComboBox一项被选中时产生ItemEvent,等等。和原生事件不同,它们并不被派发到系统事件队列中,而是由组件直接触发。事件处理器作为组件的观察者添加到组件上并侦听触发的事件。假设事件名叫XXXSwing中实现这个模式的一般模式是:
1.定义一个XXXEvent
public class XXXEvent extends Event{
    ...
    public void XXXEvent(Object src){
        super(src);
       javaswing实现购买 ...
    }
    ...
}
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){
            ve(listener);
        }
        //定义各种触发(fire)action1的方法,注意一般使用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));
        ...
    }
}
        XXXEventXXXListeneraddXXXListenerremoveXXXListener以及各种fireAction函数多是重复性代码,有些Java IDEJBuilder中能够根据开发者的指定参数的自动生成这些代码。
        实际上这个观察者模式的编程范式可以推广到任何JavaBeans,不一定是可视化的Swing组件。以前曾经见过JBuilder做的一个所谓数据库操作的JavaBeans,它没有界面,但它和Swing组件完全一样添加删除处理器。它的功能是异步操作数据库,在数据操作完了之后触发注册在上面的事件处理器,该事件处理器就可以将查询结果展现在表格中,或者输出成报表等等。
        在这个模型中,JavaBeans本身既可以是事件源(被观察对象),也可以是事件处理器(观察者),JavaBeans也可以侦听自身的事件并且处理。比如前面文章所提的MyButton在处理鼠标事件时就是自己侦听自己发出的鼠标事件,自己既是事件源,又是事件处理器,形成自反系统。各种各样的JavaBeans通过这种机制联系成一张事件网,各种J
avaBeans就是这个网上的节点,而它们之间的事件触发与事件处理关系就是这张网络上的线。当某个节点被外界或自身发出的事件所触发时,行成了事件的传播。这个过程很像网络上节点的振动引起周围周围节点振动的模型。下图示意了这种JavaBeans之间的事件网:
        例如new JscrollPane(new JtextArea())这个系统,它里面包括两个JScrollBar和一个JTextArea,当鼠标拖动事件触发JScrollBar时,JScrollBar处理了这个鼠标拖动事件,并发出滚动条拖动事件,这个事件传播给JTextAreaJTextArea处理这个拖动事件,相应的更新自己显示的内容,如果JTextArea之后又根据更新发出了一个新的事件,这个事件便会继续传播下去。
Swing布局管理器
        现在高级图形用户界面工具一般都包括布局管理器机制。什么叫做布局管理器?如果所有窗口的大小是不变的,那么我们在往窗口中添加组件时,只要将组件的拖放到固定位置、调整好尺寸就可以了,就像VB的界面工具一样。可大多数情况并非如此,用户经常需要调整窗口的大小,以便和其他程序协同工作。这种情况下,在传统界面工具中,比如VB,就需要显式的侦听窗口尺寸调整事件,根据当前窗口的大小重新计算并调整各个组件的大小和位置。AWT/SWT/Swing将这个过程自动化、模块化了,抽象出一个布局管理器来负责管理界面组件的布局。
        它们实现原理是相似的:容器类组件侦听初始化、invalide/validate以及容器尺寸调整等事件,一旦发生这些事件,容器类组件检查自己是否配置了布局管理器,如果没有,则不做任何事情;如果有,则将容器内组件的布局代理给布局管理器,让它来完成容器内组件的重新布局。
        容器管理器对象对实现两类接口:LayoutManagerLayoutManager2LayoutManager2LayoutManager的一个扩展,允许组件在添加时指定位置参数。它们的
定义和含义如下:
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 {
    //添加组件compconstraints用作指定如何以及位置的参数,这个函数主要是弥补Layou
tManager版的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);


}
        Swingjava.awtjavax.swing中都分别提供大量的布局管理器,这些布局管理器有简单的如FlowLayout,有复杂的如GridBadLayout。用户还可以自己定义自己的布局管理器,由于篇幅原因,这儿略去例子。
        Java 6中在布局管理中引入了BaseLine / Anchor的概念,能协助Java IDE的用户界面设计工具,方便用户来设计布局组件。NetBeansMatisse组件首先引入了一个GroupLayout布局管理器,结合Matisse使用,提供了非常方便的布局管理和界面设计。GroupLayoutBaseLine/Anchor概念以及Matisse可以说是Java界面设计工具的一大进步,可以说足以成为Java桌面应用史上的一个里程碑。在这之前,缺乏有力的界面设计工具是Java在桌面应用失败的一个重要原因。虽然Anchor概念早就在Delphi界面设计工具出现过,但是这个工具的出现还是Java界面设计史上的一大事件。随着Java 6桌面应用支持的增强,以及NetBeans Matisse之类界面设计工具的出现,使得Java桌面应用时代已经到来。Seeing is believing,你不妨试一下就知道了。
=====================================
        本想再加一节讲述Swing双缓冲机制,但是想到双缓冲并不是Swing模型的核心概念,没有它并不影响理解Swing的总体模型,因此打算把它作为以后的一篇专门技术文章来写。
        这样Swing模型中的Component部分就算是描述完了,从明天开始,讲述Swing模型中的另外三个重要概念:ModelUI DelegateRenderer