假如说Swing Icon接口很简单,那么其强大的功能就更令人惊奇了。通过它,你可以用程序创建图标或在图标上进行各种操作、可以用不同的方式合并图标、或在现有组件中很轻易地显示图标。让我们来开发一个组件,它可以让你显示一组图标,并可以在图标列表的各个状态中循环。在需要治理有很多元素的用户界面时(出于编辑或提供信息的目的,这些元素需要可视化地反映状态),该组件尤其有用。我们可以提供很多的例子,包括:一个日志文件条目列表,我们可以用一个彩色图标来反映每个条目的重要程度;或一个表,我们可以用一个列来反映每行的状态,包括可编辑状态、正常状态、包含在内(included )或不包含在内(excluded)的状态。
我们就不花时间来讲述如何将JIcon用做一个表、列表、树状单元绘制器(cell renderer)或编辑器了。可以说实现该功能并不难。假如我们要实现很多功能,本文的篇幅可能并不答应我们讲述太多。因此,我们将重点讲述如何构建一个可以在Icon元素中循环的简单的组件,如何运用一个你可以很轻易侦听的模式来查看事件的变化或处理单元编辑器和绘制器组件。
在开发这个项目的过程中,我实现了几个Icon工具类(utility class),你可以下载它们。CompondIcon类可以让你合并任何两个图标,形式可以是并排的,或者一个图标在另一个图标的上面。DecoratorIcon可以让你用一个图标覆盖另一个图标,就像我们在快捷装饰的例子中所做的那样(见图1)。FilterIcon可以让你在绘制图标前,将一个图像过滤器(image filter)用于图标。在继续讲述前,让我们先来简要地看看每个类的功能是怎样的。Icon接口(Swing集合的一部分)是非常简单的:public interface Icon
{
public int getIconHeight();
public int getIconWidth();
public void paintIcon(
Component c, Graphics g,
int x, int y);
}
?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
只要调用组件知道图标的宽度和高度,它就可以很好地治理版面布局了。在绘制图标时,我们调用了paintIcon()方法。大多数情况下,你需要做的就是运用绘图环境(graphic context),在指定的x、y坐标上画图,将图像控制在由getIconWidth()和getIconHeight()方法返回的宽度和高度范围内。有时侯,可以访问绘制图标所位于的组件是很有用的;例如,我们可以用Component的getBackground()方法来取得背景颜色。
通过这个接口,我们可以很轻易地从图像构建图标。实际上,Swing为此提供了ImageIcon。我们也可以很轻易地在瞬间绘制图标。我提供了一个CheckBoxIcon对此加以说明,它可以绘制一个方框,该方框可以是空的、可以包含一个复选标记、或里面画有一个X标记。我们将用这些图标来说明多个状态。你可以在图1左边底部的方框中看到它的实际应用情况。假如你点击这个图标,它就会转换到每个可能出现的状态。在每个状态中,都运用了CheckBoxIcon的一个单独的实例。JIcon组件可以治理状态。
装饰图标
让我们很快地来看看很像容器的这三种图标的实现情况吧。它们可以让你组合、覆盖或调整图标,并将它们看做是单独的Icon实例。例如,你可以用CompoundIcon将多个图标放在一个通常只处理一个图标(如Jlabel或Jbutton)的Swing组件中。
DecoratedIcon类可以让你用一个图标覆盖另一个图标。该功能可以让你用一个较小的图标在九个可能的位置上来装饰一个大的图标:垂直方向上的TOP、BOTTOM或CENTER,以及水平方向上的LEFT、RIGHT或CENTER。构造器需要两个图标实例,并需要垂直对齐和水平对齐。我们会查看对齐是否有效,假如对齐不好,或装饰图标比被装饰图标大,你就会得到IllegalArgumentException异常。我们用GetIconWidth()和getIconHeight()来返回被装饰图标(两个图标中较大的那个)的宽度和高度。然后用paintIcon()方法先绘制被装饰的图标,再在指定的位置上绘制装饰图标。
FilterIcon类可以将一个ImageFilter用于一个指定的Icon实例。这是在构造器中实现的,另外,它还缓存了结果图像,以便在绘图时使用。我选用了ImageFilter,它是老的AWT的一部分,因为通过它,我们也可以用Java 2D最近提供的BufferedImageFiler(BuffereredImageFilter实现了ImageFilter接口)。接下来的工作就是返回所创建的图像的宽度和高度了,并在调用paintIcon()方法时,在x、y位置上画图。
我刚刚讲述的三个类可以让你在现有的Swing组件中显示合并的、被装饰的和被过滤的图标。我们可以嵌套这些Icon的实现形式,从而根据你的需要来组合或调整图标。遗憾的是,没有一个Swing组件可以提供多个状态视图,因此下面就让我们来看看如何实现这样一个组件,它可以让你处理多个图标来显示与你的应用程序相关的状态。
我们来看看所实现的JIcon中的两个重要的类,IconListModel和JIcon类本身IconListModel是个接口,它可以存储多个用于JIcon的图标。它可以以列表中当前索引的形式来治理数量不定的一列图标实例和当前选择的状态。要实现一个IconListModel接口,我们也必须将事件发送到任何注册的ChangeListener以便反映状态或Icon列表内容的变化,从而使视图(如JIcon)可以立即反映这些变化。注重,IconListModel扩展了Icon接口:public interface IconListModel
extends Icon
{
public int getIconCount();
public int getCurrentIndex();
public Icon getIcon(int index);
public void setIcons(
Icon[] icon);
public void addIcon(Icon icon);
public void removeIcon(
Icon icon);
public void setCurrentIcon(
int index);
public void addChangeListener(
ChangeListener listener);
public void removeChangeListener(
ChangeListener listener);
}
通过这个接口,我们就可以很轻易地看到需要什么步骤来实现它了,即IconList的作用(见列表1)。我们运用了两个ArrayList实例:一个用来治理ChangeListener实例的列表,另一个用来治理Icon实例的列表。我们将宽度和高度值缓存起来,当调用Icon的getIconWidth()和getIconHeight()方法时,返回这些值。宽度和高度是作为图标列表的最大值通过calculateSize()方法计算的,当列表内容发生改变时,就会调用该方法。
我们也保留当前图标和索引值来反映当前选择的状态。当paintIcon()方法在Icon接口被调用时,就会绘制所选择的图标。假如有必要,我们还提供了一个setIcons()方法来设置整个列表的内容。两个构造器中的第二个构造器在初试化时用该方法来设置列表。另一个构造器是空的,需要在一个单独的步骤中填充列表。调用addIcon()或removeIcon()方法时,也会调用calculateSize()和fireChangeEvent()方法。我们运用了一个实用的方法(capIndex())来确保当前索引值永远不会超过列表的大小。FireChangeEvent()、addChangeListener()和removeChangeListener()方法治理注册的ChangeListener的列表。
突出焦点
缺省情况下,在JIcon类中,我们用一个单独的像素EmptyBorder环绕在图标四周,这样我们就可以用一个蓝色的LineBorder来重点显示图标了。你可以调用setFocusable()方法来激活该行为。缺省情况下,我们可以处理鼠标事件(mouse event),但不接受聚焦显示。通过三个构造器我们可以创建一个空的JIcon、带有一个单独的图标的JIcon或带有一个初始Icon列表的JIcon。在每种情况中,我们都用initComponent()方法来设置可选的美学效果和监听器(listener)。
JIcon实现了四个监听器:ChangeListener、FocusListener、MouseListener和KeyListener。触发ChangeListener事件可以重画视图。我们用了一个叫做resetSize()的方法,因为模式的变化会影响由组件返回的首选的、最小的尺寸。实际的尺寸是由IconList缓存的,当图标被添加或删除时,IconList就会计算实际尺寸,因此首选的、最小的尺寸就会根据当前的getIconWidth()和getIconHeight()方法值被更新,并根据现有边界进行调整。
假如有必要,可以用FocusListener的focusGained()和focusLost()方法来改变边界。我们可以通过调用标准的isFocusable()方法来查看是否可以运用该行为。注重,Java 1.4对setFocusable()和isFocusable()方法都有介绍。假如你需要向后兼容(backward compatible),你必须修改代码来运用isFocusTraversible()方法。
KeyListener和MouseListener方法可以用来响应用户的输入。对于KeyListener来说,我们可以通过调用nextIcon()方法对空格键作出响应。MousePressed事件的作用是一样的,但先要有个焦点(focus)。在这两种情况中,我们都调用重画方法来刷新视图。我在实现的例子中已经提供了nextIcon()和prevIcon()方法,虽然我们只用了nextIcon()方法。
NextIcon()方法从模式中提取当前索引值,并以增值(increment)顺序取下一个索引,假如当前索引位于列表的最后,则运用nextIcon()会返回到第一个值。PrevIcon()方法的功能正好相反,它以递减(decrement)的顺序取列表中的前一个值,假如当前索引是列表中第一个值,则prevIcon()会取列表的最后一个值。在我实现的这个例子中,我们没有用prevIcon()方法,但是你可能想让用户通过后退键和鼠标右键在列表中进行后退(move backward)操作。
以上就是有关JIcon类的简要内容了,另外还有accessor,它们可以让你get和set模式、在列表中添加、删除和设置图标;还有setBorder方法,假如边界不为空,我们可以覆盖该方法来调用resetSize方法。SetModel()方法可以在用新模式注册前注销以前的任何模式。实际效果是,你可以直接通过程序设置模式的索引值、通过用户键入或鼠标事件,用一个组件来循环一列图标。
JIcon组件填补了Swing的空白,我们可以用它来治理图标列表,这些图标可以以一种可视的和可编辑的方式来反映状态。JIcon可以给我们开发的很多复合Icon实例带来好处,使我们可以实现更复杂的视图而不会增加复杂性,但这些实现方法也可以用于运用Icon接口实例的任何组件,如JLabel和JButton。JIcon可以治理状态,但IconListModel也可以实现Icon接口,并可以用在其它组件中。你在寻找可以提高可用性的方法时,我希望这些技术和例子会对你有所帮助。