模型过滤的基本概念利用了 Swing 组件对模型类的底层实现缺乏了解这一事实。下图说明了这种典型的关系:
模型过滤器是实现了模型接口、但并不真正包含数据的类。模型过滤器在组件与其模型之间进行协调。模型过滤器可以重新解释模型所提供的信息,并且可以更改所提供的数据元素个数、数据的顺序以及数据本身。
在本例中,过滤器类是将一个现有模型类作为其数据源来实例化的。在模型过滤器的一般实现中,对 API 方法的调用将委托给源模型。
由于此 API 是统一实现的,因此完全可以在组件与其模型之间“叠放”多个过滤器。注意,每个过滤层都要求每个 API 调用穿过一个附加的间接层;如果过滤层过于复杂,则很可能造成性能瓶颈。
基本过滤器
下面显示的抽象类是作用于 JList 组件之上的模型过滤器的基类。其唯一的构造函数要求,模型过滤器的每个实例都要引用某个底层的模型数据。该数据既可以是另一个模型过滤器,也可以不是;在这两种情况下,过滤器的行为是相同的。
模型过滤器基类
2 import javax.swing.*;
3 public abstract class AbstractListModelFilter extends AbstractListModel
4 {
5 // 用来保存被过滤模型的引用
6 protected ListModel delegate;
7 // 构造函数 ― 接受单个参数,其中包含被过滤模型的引用
8 public AbstractListModelFilter(ListModel delegate)
9 {
10 this.delegate = delegate;
11 }
12 public ListModel getDelegate()
13 {
14 return this.delegate;
15 }
16 public int getSize()
17 {
18 // 委托给过滤器目标
19 return delegate.getSize();
20 }
21 public Object getElementAt(int index)
22 {
23 // 委托给过滤器目标
24 return delegate.getElementAt(index);
25 }
26 public void addListDataListener(ListDataListener listener)
27 {
28 // 委托给过滤器目标
29 delegate.addListDataListener(listener);
30 }
31 public void removeListDataListener(ListDataListener listener)
32 {
33 // 委托给过滤器目标
34
35 delegate.removeListDataListener(listener);
36 }
37 }
38
该类相当于一种“空”过滤器,它不更改任何底层数据。因此,它没有什么特别的意义。ListModel 过滤器类的实际实现将覆盖该抽象类的方法,以便以不同的方式呈现底层数据。
您可以通过实现过滤器来改变底层数据事件的特性。为了使对模型过滤器的讨论更易于理解,本文的示例都只针对不可变的数据模型,即不触发任何模型事件的类。
缺省模型适合于要求不高的一般应用。但是,您应该了解这些缺省类都是为通用目的而设计的,因此,在对性能有严格要求的情况下,它们通常表现不佳。同样,许多常用的模型都是作为可变模型来实现的,即,模型的数据可随时间变化。当已知数据为静态数据时,这些额外的行为可能是多余的。因此,您可能想另外构建模型类,去掉由事件传播所导致的额外开销。
不可变模型
在许多情况下,根据模型的底层数据是否可变对模型进行分类很有用。在数据不会变化的情况下,可以实现不可变的数据模型,这种模型不实现用于监听数据变化的监听程序。Swing 模型接口的缺省实现假定数据是可变的。
不可变模型的创建过程相当简单。您可以创建一个具体类,该类可提供模型接口,但为与事件相关的活动所提供的所有方法都不执行任何操作。根据模型要作为一般模型使用,还是作为专用模型使用,您既可将此不可变模型实现为一个抽象类,也可将其实现为一个具体类。
下面的示例是一个不可变的列表模型,我设计它时希望它非常通用,并且允许将支持 java.util.List 集合接口的任何对象用作数据源。返回的数据是一个笼统的 Object 类型;如何显示对象留待 JList 及其相关绘制程序解释。
不可变模型的示例
2 import java.util.*;
3 import javax.swing.*;
4 public abstract class ImmutableListModelFilter extends AbstractListModel
5 {
6 // 用来保存被过滤模型的引用
7 protected List collection;
8 // 构造函数 ― 接受单个参数,其中包含被过滤模型的引用
9 public AbstractListModelFilter(List collection)
10 {
11 this.collection = collection;
12 }
13 public List getCollection()
14 {
15 return this.collection;
16 }
17 public int getSize()
18 {
19 // 委托给集合
20 return collection.size();
21 }
22 public Object getElementAt(int index)
23 {
24 // 委托给过滤器目标
25 return collection.get(index);
26 }
27 public void addListDataListener(ListDataListener listener)
28 {
29 // 覆盖为‘空操作’
30 }
31 public void removeListDataListener(ListDataListener listener)
32 {
33 // 覆盖为‘空操作’
34 }
35 }
36
下面将讨论四种类型的过滤器:替换、排序、排除和包含。