Swing模型过滤器概述

开发 后端
本文介绍Swing模型过滤器,模型过滤是这样一种技术,它在 Swing 组件体系结构中提供附加的功能与灵活性。

Swing 体系结构的重要创新之一在于采用了模型/视图/控制器 (MVC) 原理,这样就可将组件的不同角色分离开。当一种体系结构具备 MVC 分离特性时,即可对组件的数据与状态作不同的解释。这允许程序员在组件及其模型之间插入过滤器对象。模型过滤可以在模型内修改数据的表示,还也可以改变模型所封装数据的外在数目和顺序。

Swing模型过滤器的另外两种重要特性是:

模型过滤操作不会改变底层的模型数据。这使得多个组件可以共享一组数据,而且每个组件都可能以不同的方式解释这组数据。

过滤器可以叠用,这样就可以依次用几个不同的过滤器对象来解释模型数据。

已定义的代理

为了***限度地利用 Java 平台对面向对象的支持,可以简单地认为组件由若干对象构成。这些对象可以由一个通用术语 ― 代理 ― 来描述。代理是实现一个公共 Java 接口并与某个特定组件相关联的对象。代理实现的接口定义代理在 MVC 体系结构中充当的角色。

对于刚刚接触 Swing 的程序员而言,代理的概念似乎有些难以理解,但是,它们也是 AWT 组件的一种共同特征。例如,如果想更改 java.awt.Label 组件上的字体,只需创建或获取 java.awt.Font 类的一个实例,并且调用 getFont() 使该实例与组件相关联。Font 对象的内部运作细节可能很有趣,但是组件只要有 Font 类型对象的一个引用即可适当地显示自己。甚至像标签前景颜色这种简单概念也是通过代理实现的;java.awt.Color 类提供一种适合作组件前景颜色的对象。作为一般规则,值为非基本数据类型的各种组件属性都可看作是代理。

Swing 中的 MVC 实现就是这些概念的体现。对象不仅用于表示组件的属性值,也用于表示组件行为的诸多方面。这种方案相当灵活,足以支持 Swing 的可插接外观 (PLAF) 功能的实现,该功能使应用程序既可模拟本地平台的外观,也可用一种与平台无关的方案显示组件。PLAF 既可使应用程序看起来就像 Microsoft Windows、 Mac OS 和 X/Motif 等平台的本地应用程序一样,也可使应用程序具有一种中立的外观,称为 "Java" LAF 或 "Metal" LAF。

PLAF 功能与组件的外观密切相关。本文主要讨论这一体系结构的模型部分,它与组件的外观的无关。

作为一种模型(或类似一种模型)

每种支持数据与状态的 Swing 组件都有一种与之相关的模型接口。无论接口感兴趣的是封装于该模型的数据还是状态,它都会包含允许组件以编程方式查询模型内容的若干方法。

每个模型接口都提供两类方法:一类方法提供对数据与状态的访问,而另一类方法允许组件或者其他对象注册或取消注册事件监听程序。监听程序的类型及其提供的事件对象都由这些方法定义。

Swing 模型接口可以有不同类型的类实现。在许多情况下,为模型提供的是一种抽象实现;除了为了触发模型所表示的各种事件方法而提供的 protected 方法之外,这通常是一种不完全的正则实现。所有模型都有一个缺省实现,并且是一个具体类。

既好又简单 ― ListModel 接口

在开始讨论过滤之前,对典型的模型接口作一回顾不失为明智之举。

ListModel 接口代表 JList 组件中的数据。这是三种集合模型中最简单的一种。(另外两种分别是 JTree 和 JTable。) ListModel 有两个方法用于检索列表中的元素个数以及各个元素,另外还有两个方法用于维护感兴趣的监听程序列表,以便监听列表模型的变化。

ListModel 的简化源代码

  1. package javax.swing;  
  2. import javax.swing.event.ListDataListener;  
  3. public interface ListModel  
  4. {  
  5. int getSize();  
  6. Object getElementAt(int index);  
  7. void addListDataListener(ListDataListener listener);  
  8. void removeListDataListener(ListDataListener listener);  

在 ListModel 接口中, getSize() 与 getElementAt() 方法用于遍历模型中的元素,而其他两个方法用于建立与感兴趣的监听程序之间的关联,以便监听模型的变化。

ListDataListener 接口支持三个方法,当模型监听到其底层数据发生变化时就会调用这三个方法。这三个方法是 intervalAdded() 、 intervalRemoved() 和 contentsChanged() ,每个方法都接受单个 ListDataEvent 作为参数。根据模型所发生变化的复杂程度之不同,模型实现可以使用其中的任一个方法来描述这些变化。通常, intervalAdded() 和 intervalRemoved() 用于描述变化的时间间隔;当变化过于复杂,无法作为一个闭合间隔进行描述时,就会用到 contentsChanged() 。

为了理解模型过滤如何运作,请记住这一点:JList 组件只对 ListModel API 的实现感兴趣。该组件并不关心数据驻留何处以及数据是如何组织的。无论该模型是一个缺省类、抽象类的扩展,还是 ListModel 接口的一种直接实现,都不影响 JList 组件的行为

模型过滤的基本概念利用了 Swing 组件对模型类的底层实现缺乏了解这一事实。下图说明了这种典型的

Swing 组件对模型类的典型例子

Swing模型过滤器是实现了模型接口、但并不真正包含数据的类。模型过滤器在组件与其模型之间进行协调。模型过滤器可以重新解释模型所提供的信息,并且可以更改所提供的数据元素个数、数据的顺序以及数据本身。

数据元素个数、数据的顺序以及数据本身

在本例中,过滤器类是将一个现有模型类作为其数据源来实例化的。在模型过滤器的一般实现中,对 API 方法的调用将委托给源模型。

由于此 API 是统一实现的,因此完全可以在组件与其模型之间“叠放”多个过滤器。注意,每个过滤层都要求每个 API 调用穿过一个附加的间接层;如果过滤层过于复杂,则很可能造成性能瓶颈。

基本过滤器

下面显示的抽象类是作用于 JList 组件之上的模型过滤器的基类。其唯一的构造函数要求,模型过滤器的每个实例都要引用某个底层的模型数据。该数据既可以是另一个模型过滤器,也可以不是;在这两种情况下,过滤器的行为是相同的。

Swing模型过滤器基类

  1. package com.ketherware.models;  
  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. delegate.removeListDataListener(listener);  
  35. }  

该类相当于一种“空”过滤器,它不更改任何底层数据。因此,它没有什么特别的意义。ListModel 过滤器类的实际实现将覆盖该抽象类的方法,以便以不同的方式呈现底层数据。

您可以通过实现过滤器来改变底层数据事件的特性。为了使对模型过滤器的讨论更易于理解,本文的示例都只针对不可变的数据模型,即不触发任何模型事件的类。

缺省模型适合于要求不高的一般应用。但是,您应该了解这些缺省类都是为通用目的而设计的,因此,在对性能有严格要求的情况下,它们通常表现不佳。同样,许多常用的模型都是作为可变模型来实现的,即,模型的数据可随时间变化。当已知数据为静态数据时,这些额外的行为可能是多余的。因此,您可能想另外构建模型类,去掉由事件传播所导致的额外开销。

不可变模型

在许多情况下,根据模型的底层数据是否可变对模型进行分类很有用。在数据不会变化的情况下,可以实现不可变的数据模型,这种模型不实现用于监听数据变化的监听程序。Swing 模型接口的缺省实现假定数据是可变的。

不可变模型的创建过程相当简单。您可以创建一个具体类,该类可提供模型接口,但为与事件相关的活动所提供的所有方法都不执行任何操作。根据模型要作为一般模型使用,还是作为专用模型使用,您既可将此不可变模型实现为一个抽象类,也可将其实现为一个具体类。

下面的示例是一个不可变的列表模型,我设计它时希望它非常通用,并且允许将支持 java.util.List 集合接口的任何对象用作数据源。返回的数据是一个笼统的 Object 类型;如何显示对象留待 JList 及其相关绘制程序解释。

【编辑推荐】

  1. Swing组件相关的操作
  2. TikeSwing创建MVC体系结构
  3. 全面介绍Swing容器
  4. 浅析Accelerator key in Java Swing
  5. JFC和Swing中的JLabel组件
责任编辑:佚名 来源: 中国IT实验室
相关推荐

2021-07-05 15:22:03

Servlet过滤器客户端

2024-01-05 09:04:35

隆过滤器数据结构哈希函数

2009-07-08 15:30:56

Servlet过滤器

2009-09-29 13:55:23

Hibernate设置

2009-07-08 16:07:04

Servlet过滤器配

2011-06-29 16:14:59

Qt 事件 过滤器

2009-06-18 10:13:00

Hibernate过滤

2009-07-08 17:33:37

Servlet过滤器

2009-09-25 15:19:44

Hibernate过滤

2017-07-18 14:10:31

大数据Apache Flum过滤器

2009-07-09 11:55:17

Swing模型过滤

2009-07-06 13:02:49

Servlet过滤器

2024-03-15 11:21:22

布隆过滤器数据库数据

2023-01-26 01:41:27

核心全局过滤器

2016-12-07 09:56:13

JavaFilter过滤器

2010-03-01 14:45:07

Linux文件重定向

2017-04-12 14:43:01

Spring ClouZuul过滤器

2010-12-27 13:14:15

Openbsd PFOpenBSD数据包过滤

2009-07-03 18:26:11

Servlet过滤器

2021-01-14 08:13:39

Spring Clou应用内置过滤器
点赞
收藏

51CTO技术栈公众号