轻量级通知
下面Swing中的模型使用轻量级通知,它们是基于 ChangeListener、ChangeEvent接口的:
Model | Listener | Event |
BoundedRangeModel | ChangeListener | ChangeEvent |
ButtonModel | ChangeListener | ChangeEvent |
SingleSelectionModel | ChangeListener | ChangeEvent |
ChangeListener接口只有一个通用方法:
ChangeEvent中仅有的状态是事件源,因为所有通知中的事件源都是相同的,单独一个事件实例可以用作所有来自该模型的通知。使用此机制的模型支持下面的方法来添加和删除ChangeListeners:
public void addChangeListener(ChangeListenerl)public voidremoveChangeListener(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接口和事件对象。下表是这些模型接口和事件对象的类:
Model | Listener | Event |
ListModel | ListDataListener | ListDataEvent |
ListSelectionModel | ListSelectionListener | ListSelectionEvent |
ComboBoxModel | ListDataListener | ListDataEvent |
TreeModel | TreeModelListener | TreeModelEvent |
TreeSelectionModel | TreeSelectionListener | TreeSelectionEvent |
TableModel | TableModelListener | TableModelEvent |
TableColumnModel | TableColumnModelListener | TableColumnModelEvent |
Document | DocumentListener | DocumentEvent |
Document | UndoableEditListener | UndoableEditEvent |
Listener除了可以直接查询事件对象来跟踪内容改变外,这些API的作用与轻量级通知相似。比如下面的代码动态的跟踪JList被选中的项:
Stringitems[] = {"One", "Two", "Three");
JList list = new JList(items);
ListSelectionModel sModel =list.getSelectionModel();
sModel.addListSelectionListener(new ListSelectionListener() {
publicvoidvalueChanged(ListSelectionEvent e) {
// get change information directly
// fromtheevent instance...
if (!e.getValueIsAdjusting()) {
System.out.println("selection changed: " +
e.getFirstIndex());
}
}
});
自动视图更新
模型没有任何表现它视图的固有知识,相反模型只有关心其状态改变的Listener列表,这种需求对于同个模型多个视图的框架来说是至关重要的。Swing组件负责将合适的模型Listener连接起来,以便于模型改变时能正确地重画出自己。如果你发现模型改变时,组件不能自动更新,说明组件的实现就存在错误。
忽略模型
正如前面提到的,大多数组件直接在Component类中提供模型定义的API,以方便组件能不用和模型交互就直接操作,这是相当可行的编程方法,尤其是对于GUI状态模型来说。比如下面的JSlider内部getValue的实现,它将调用代理给模型:
public int getValue(){return getModel().getValue();
}
因此程序完全可以这样写:
JSlider slider = new JSlider();int value=slider.getValue();
Swing模型总结
虽然理解了Swing模型设计是如何工作的,但没有必要在所有Swing编程中都使用模型API。你需要注意考虑应用程序各自的需求,决定哪儿使用模型API能帮你提升代码,且不带来不必要的复杂性。
我特别推荐在Swing中使用应用数据模型(如JTable和JTree等的模型),因为从长期来看,它们能极大地提高你的应用程序可扩展性和模块化度。