Swing模型与渲染器

模型(Model)

         直接操作Swing的模型(Model)对于编写可扩展的用户界面至关重要,下面代码是往JComboBox添加数据项的通常做法:

JComboBox box = new JComboBox();
for (int i = 0; i < numItems; i++) {
    box.addItem(new Integer(i));
}

         这些代码只是简单的往JComboBox中添加数据项,代码同往AWT的Choice中添加选项类似,这种方法对于小数据量来说可以,但是当要添加大量数据时就会明显变得非常慢。

         尽管上面的代码没有明确引用任何模型,JComboBox的模型对象实际上参与这个过程,每次调用addItem时,JComboBox内部发生了许多操作:组件将请求传递给JComboBox的模型,模型发送一个事件表明一个新项被添加。很明显,如果你直接操作模型的将会更高效,如下例所示:

Vector v = new Vector(numItems);
for (int i = 0; i < numItems; i++) {
   v.add(new Integer(i));
}
ComboBoxModel model = new DefaultComboBoxModel(v);
JComboBox box = new JComboBox(model);

         这样为什么会更快呢?原因有两个。第一,因为所有项是一次添加到模型去,而不是一个一个的,只有一个事件发出,这意味着更少的事件触发,更少的方法调用。第二是因为需要通知变化的对象更少,总的工作量等于触发次数乘以侦听器数目。因为模型是新创建的,侦听在上面的侦听器为零,这意味着没有触发事件发生。

         从上面的例子可以学到两点:

  • 尽可能使用批操作,尽量减少触发事件的数量。

  • 当初始化或者需要完全替换模型的内容时,考虑重新生成模型,不要使用已经存在的模型,已存在模型上已经保持了很多的侦听器,新生成的模型没有侦听器,这样避免了不必要的处理函数的调用。

             触发事件数量严重影响你的程序启动时间,也会影响打开对话框和相似操作的时间。

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

 今天从Swing的四大基本对象Component说起。

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

         Component在Swing的MVC模型中担任Controller的角色,同时它也是Swing API中代表具体组件的对象。Component在Swing中对外负责提供API接口,对内负责协调控制Model和UI Delegate(有时可能还包括Renderer)的操作,可以说是整个Swing结构的中心角色。

Swing框架之Component

         Component代表Swing对应用程序提供了如下几类编程接口:

  1. 用户界面的组件树的创建和修改的方法。这包括组件的添加和删除等操作。
  2. 组件属性访问的方法,比如组件位置、组件前后背景色、组件字体等等。
  3. 组件状态及生命周期的管理的方法,比如隐藏和显示、创建和销毁等等。
  4. 组件位置、大小的管理,包括通过布局管理器的方法。
  5. 组件事件处理接口的管理,包括添加、删除等操作。

         从开发者的角度来看,Component是组件树上的节点、是控制外观和行为的入口、是组件事件的发源地。从Swing组件实现者的角度来看, Component是协调Model和UI Delegate的操作的地方,是低层次事件处理的地方,是高层事件发生的地方,是同父组件和子组件交互的地方。掌握的这些角度,Swing程序员完全可以实现自己的自定义简单组件,当然如需要实现类似于JTable和JTree等复杂的矢量组件,还需要进一步了解Swing的Model和UI Delegate以及Renderer模型。

         对于复合型(Composite)组件很简单,这儿要讲述的是如何实现自定义的组件,比如表盘,比如温度计这些没有标准组件可以使用的组件,那么如何自己实现这种自定义组件呢?

         不考虑Model分隔和UI Delegate皮肤分离问题,能够简化自定义Component的模型。总的来说自定义组件需要完成两样基本任务:第一侦听并处理低层事件,根据具体情况改变组件状态,如需要还要发出高级事件;第二,根据当前组件的状态画出当前组件的外观。

         侦听底层的事件是指侦听类似于mouse、keyboard、focus等事件,然后处理此事件,如果发现此事件带有特定语义,表达某种组件行为,则改变当前的组件状态以记录,并触发某种事件通知应用程序进行处理。举例说明,想象你准备实现一个简单的按钮,你可以通过继承JComponent来完成。你可以在按钮初始化时,注册此按钮的鼠标事件侦听器,以侦听发生自己组件上的鼠标事件。当按钮捕获到鼠标按下时,检查鼠标按下的点是否在按钮有效区域内。如果是,则认为当前是一个按钮按下动作,那么改变按钮的状态为按下去,调用repaint方法通知按钮重画成按下去的状态,然后发出 ActionPerformed的事件,通知注册在此按钮上的应用程序的ActionListener处理这个动作。下面是一个简单示意代码:

public class MyButton extends Jcomponent implements MouseListener{
    private String text;
    private boolean pressed=false;
    private ArrayList<ActionListener> listeners=new ArrayList<ActionListener>();
    public MyButton(){
        addMouseListener(this);//将自己注册为自己的鼠标事件侦听器,监听鼠标事件
    }
    ....
   public void mousePressed(MouseEvent evt){
        Point p=evt.getPoint();
        if(getBounds().contains(p)){//判断鼠标落点是否在有效区域内。
            pressed=true; //鼠标点击的含义是按钮被按下!改表按钮状态。
            repaint();    //通知按钮重画,由原来的抬起状态改变成按下状态。
            fireActionPerformed(new ActionEvent(this)); //这是一个按钮动作事件,触发它。
        }
   }
   public void addActionListener(ActionListener listener){
        listeners.add(listener);
   }
   public void removeActionListener(ActionListener listener){
        listeners.remove(listener);
   }
   protected fireActionPerformed(ActionEvent evt){
        for(ActionListener listener:listeners){
            listener.actionPerformed(evt);
        }
   }
   ...
   //这儿你要覆盖paint方法,实现按钮状态的重画
   public void paint(Graphics g){
       if(pressed){
         //画出按下的样子
       }else{
         //画出抬起的样子
       }
   }
}

         上面要注意的是你要自己管理自定义组件的事件监听器,包括addListener和removeListener方法,以及如何触发。这个过程很简单,基本上就是上面的模板来实现添加删除和触发。

         除了要负责事件的处理和新事件的触发,自定义组件第二个要完成的任务就是要根据组件当前的状态改变重画组件的外观。重画组件的外观只需要覆盖public void paint(Graphics g)方法即可以,在这个方法里,你只需要根据当前的组件状态分别画出当前的组件即可。

         当然除了上面两个基本准则外,不要忘了添加访问你的组件属性的方法,比如,如果上面的按钮是个二元按钮(相当于 JCheckbox/JToggleButton的那种按钮),你可能需要提供isPressed或者setPressed来获取和设置当前按钮的状态。注意,在设置状态按钮变化的访问方法中,比如setPressed,你需要使用repaint方法通知按钮重新渲染(复杂的实现可能包括触发propertyChange事件,这儿从简):

public void setPressed(boolean p){
    pressed=p;
    repaint();
}

         到此为止,你已经能根据上面的两条准则简单的实现你想要的组件了。但是你发现没有,你的按钮状态和外观行为都被堆到了Component (MyButton)中实现了,而且,对于各个平台都是一个样子,不能换皮肤。这对于比较简单、不想要皮肤的组件,可能没有什么,但是对于复杂的组件,比如JTable或者甚至Excel类似的电子表格的那种组件,你把数据(组件状态)和外观堆在这儿实现就严重违反了MVC原则。

         如何简化这种组件的实现呢?使你实现的此种组件容易维护、扩展以及重用,皮肤容易换呢?这就需要Swing结构中的另外三个元素:Model、UI Delegate和Renderer,后面的几个文章将讲述Model、UI Delegate和Renderer帮你逐步实现一个复杂、灵活、可扩展、高效的矢量组件。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值