Swing框架之Model

构建应用程序应该 以数据为中心,而不是以用户界面为中心,这是一个良好的编程习惯。为支持这种编程范式,Swing为每种带有逻辑数据或值的组件定义了独立的模型接口,这种分割使程序可以选择向Swing组件中嵌入自己的模型实现。

      下面表格列出Swing中组件及其模型的映射关系:

组件Model接口Model类型
JButtonButtonModelGUI状态
JToggleButtonButtonModelGUI状态/应用数据
JCheckBoxButtonModelGUI状态/应用数据
JRadioButtonButtonModelGUI状态/应用数据
JMenuButtonModelGUI状态
JMenuItemButtonModelGUI状态
JCheckBoxMenuItemButtonModelGUI状态/应用数据
JRadioButtonMenuItemButtonModelGUI状态/应用数据
JComboBoxComboBoxModel应用数据
JProgressBarBoundedRangeModelGUI状态/应用数据
JScrollBarBoundedRangeModelGUI状态/应用数据
JSliderBoundedRangeModelGUI状态/应用数据
JTabbedPaneSingleSelectionModelGUI状态
JListListModel应用数据
JListListSelectionModelGUI状态
JTableTableModel应用数据
JTableTableColumnModelGUI状态
JTreeTreeModel应用数据
JTreeTreeSelectionModelGUI状态
JEditorPaneDocument应用数据
JTextPaneDocument应用数据
JTextAreaDocument应用数据
JTextFieldDocument应用数据
JPasswordFieldDocument应用数据

Swing模型分类

      Swing提供的模型分为两大类:GUI状态模型和应用数据模型

      GUI状态模型是描述GUI控件可视化状态的接口,如按钮是否按下,或列表中那一项被选中。GUI状态模型通常仅在图形用户界面(GUI)环境中用到。通常来说,虽然编写使用GUI状态模型分离程序,尤其是当多个GUI控件共享状态,或当操作一个控件自动更新另一个的值时比较有用,但GUI状态模型在Swing中并不是必需的,完全可以通过组件顶层方法操作GUI控件的状态,而不必和模型直接交互。

      应用数据模型是描述具有应用程序含义数据的接口,比如表格中的数据,或列表显示的选项。这些数据模型为Swing提供了一个清晰分割应用程序界面和数据逻辑的强大编程模式。对于以数据为核心的Swing组件,比如JTree和JTable,强烈推荐使用数据模型进行交互。

      当然一些组件的模型根据应用场景的不同其分类介于GUI状态模型和应用数据模型之间,比如JSlider和JProgressBar的BoundedRangeModel。

      Swing的可分离模型接口并没有明确界定GUI状态模型和应用数据模型。这儿所以做此说明,目的是让你更好的理解何时以及为何要需要使用分离的模型。

共享模型定义

      值得注意的是,上文中表格中,许多组件的数据抽象相似,只需一个接口而不用过分泛化时,组件可以共享同一模型定义。共享模型定义允许在不同组件之间自动连接。比如,JSlider和JScrollBar都使用BoundedRangeModel接口,因此可以在一个JScrollBar和一个JSlider之间共享同一个BoundedRangeModel实例,这样它们之间的状态就总是同步的。


分离模型编程接口

      使用模型的Swing组件必须提供访问修改模型的set/get方法,即模型必须是该组件的限定性属性。比如,JSlider使用BoundedRangeModel接口作为它的模型定义,因此它必须提供下面方法:

   publicBoundedRangeModel getModel()
  publicvoidsetModel(BoundedRangeModelmodel)

     所有Swing组件有一个共同点:如果你不设置它的模型,组件会在内部创建/安装一个缺省模型。这些缺省模型类的命名习惯是在接口名称之前加上“Default”,比如JSlider的构造函数中初始化一个DefaultBoundedModel对象。

public JSlider(int orientation, int min, int max,intvalue){
  checkOrientation(orientation);
   this.orientation =orientation;
   this.model =newDefaultBoundedRangeModel(value, 0, min, max);
  this.model.addChangeListener(changeListener);
  updateUI();
}

      如果程序接着调用setModel(),缺省的模型就被替换了,比如下面例子:

JSliderslider = new JSlider();
BoundedRangeModel myModel = new DefaultBoundedRangeModel() {
     public void setValue(int n){
        System.out.println("SetValue: "+ n);
        super.setValue(n);
     }
   });
slider.setModel(myModel);

      对于更复杂的模型(如JTable和JList),Swing还提供一个抽象模型实现,让开发者不需要从头开始创建自己的模型。

      如JList的模型接口是ListModel,Swing同时提供了DefaultListModel和AbstractListModel两个类来协助开发者创建自定义的列表模型。

模型改变通知

      当数据或者发生变动时,模型必须通知所有相关方(比如视图)。Swing模型使用前面文章所讲述的事件模型来实现这种触发。Swing中有两种方法发送这种通知:

      发送轻量级通知,表明状态已经改变,需要Listener通过查询模型,发现什么改变了并做出响应。此方法的优点是单独事件实例能用作该模型的所有通知,同时对于需要频繁通知的事件非常有用(比如JScrollBar被拖动时)。

      发送状态化通知,详细描述模型如何改变。这种方法需要为每个通知创建一个新的事件实例。当通知通过查询模型不能有效地给Listener提供足够的信息时,此方法非常有用。比如当JTable的一列表格数据发生改变时。


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

轻量级通知

      下面Swing中的模型使用轻量级通知,它们是基于ChangeListener、ChangeEvent接口的:

ModelListenerEvent
BoundedRangeModelChangeListenerChangeEvent
ButtonModelChangeListenerChangeEvent
SingleSelectionModelChangeListenerChangeEvent


     ChangeListener接口只有一个通用方法:

public voidstateChanged(ChangeEvent e)

     ChangeEvent中仅有的状态是事件源,因为所有通知中的事件源都是相同的,单独一个事件实例可以用作所有来自该模型的通知。使用此机制的模型支持下面的方法来添加和删除ChangeListeners:

   public voidaddChangeListener(ChangeListenerl)
   publicvoidremoveChangeListener(ChangeListenerl)

       获知JSlider数据发生变化的代码可以使用如下代码实现:

JSlider slider = new JSlider();
BoundedRangeModelmodel=slider.getModel();           
model.addChangeListener(new ChangeListener() {
     public voidstateChanged(ChangeEvent e) {
           // need to query themodel
           // to getupdatedvalue...
           BoundedRangeModel m=(BoundedRangeModel)e.getSource();
           System.out.println("model changed: "+
           m.getValue());
     }
});

      为给不想和分离式模型交互的程序提供方便,一些Swing组件类提供了直接在组件上注册ChangeListener的方法(组件可在组件内部侦听模型的数据变化,并将事件传播给任何注册在组件上的Listener),这些通知的唯一区别是,使用模型注册方式的事件源是该模型实例,而使用组件注册方式的事件源是该组件。

       因此我们可以将前面的例子简化成:

JSlider slider = newJSlider();
slider.addChangeListener(new ChangeListener() {
    public void stateChanged(ChangeEvent e) {
          //the source will be
          // the slider this time..
          JSlider s=(JSlider)e.getSource();
          System.out.println("valuechanged: "+s.getValue());
    }
});

状态化通知

      支持状态化通知的模型根据它们的目的提供不同的Listener接口和事件对象。下表是这些模型接口和事件对象的类:

ModelListenerEvent
ListModelListDataListenerListDataEvent
ListSelectionModelListSelectionListenerListSelectionEvent
ComboBoxModelListDataListenerListDataEvent
TreeModelTreeModelListenerTreeModelEvent
TreeSelectionModelTreeSelectionListenerTreeSelectionEvent
TableModelTableModelListenerTableModelEvent
TableColumnModelTableColumnModelListenerTableColumnModelEvent
DocumentDocumentListenerDocumentEvent
DocumentUndoableEditListenerUndoableEditEvent

      Listener除了可以直接查询事件对象来跟踪内容改变外,这些API的作用与轻量级通知相似。比如下面的代码动态的跟踪JList被选中的项:

Stringitems[] = {"One", "Two","Three");
JList list = newJList(items);
ListSelectionModel sModel=list.getSelectionModel();
sModel.addListSelectionListener(new ListSelectionListener(){
      publicvoidvalueChanged(ListSelectionEvent e) {
            // get change information directly
           // fromtheeventinstance...
           if(!e.getValueIsAdjusting()) {
                System.out.println("selection changed: " +
                e.getFirstIndex());
           }
      }
});


自动视图更新

       模型没有任何表现它视图的固有知识,相反模型只有关心其状态改变的Listener列表,这种需求对于同个模型多个视图的框架来说是至关重要的。Swing组件负责将合适的模型Listener连接起来,以便于模型改变时能正确地重画出自己。如果你发现模型改变时,组件不能自动更新,说明组件的实现就存在错误。

忽略模型

       正如前面提到的,大多数组件直接在Component类中提供模型定义的API,以方便组件能不用和模型交互就直接操作,这是相当可行的编程方法,尤其是对于GUI状态模型来说。比如下面的JSlider内部getValue的实现,它将调用代理给模型:

public intgetValue(){
     return getModel().getValue();
}

        因此程序完全可以这样写:

JSlider slider = newJSlider();
intvalue=slider.getValue();

Swing模型总结

        虽然理解了Swing模型设计是如何工作的,但没有必要在所有Swing编程中都使用模型API。你需要注意考虑应用程序各自的需求,决定哪儿使用模型API能帮你提升代码,且不带来不必要的复杂性。

        我特别推荐在Swing中使用应用数据模型(如JTable和JTree等的模型),因为从长期来看,它们能极大地提高你的应用程序可扩展性和模块化度。

来源: http://blog.sina.com.cn/s/blog_4b6047bc010007lo.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值