使用多路复用外观

多路复用外观让您可以用一种或多种 auxiliary 外观补充普通外观(称为 default 外观)。例如,除了基于 Swing 的应用程序生成的普通视觉输出之外,您还可以同时提供文本到语音和盲文输出,方法是添加两种辅助外观(一个用于文本到语音,另一个用于盲文)到默认的外观和感觉。默认外观可以是任何普通外观——例如 Java 或 Windows 外观——并且无需修改即可与辅助外观一起使用。

本文档包含以下部分:

在进一步阅读之前,您应该熟悉可插入外观的概念。有关基本信息,请参阅 如何设置外观The Java Tutorial 中的一个部分。有关体系结构的详细信息,您可以阅读 可插拔的外观架构,这是 Swing Connection 文章中的一个部分。


概述

javax.swing.plaf.multi 包中的类实现了多路复用外观.多路复用外观透明地创建——并同时支持——来自多个不同外观的 UI 对象,以响应组件请求其 UI 对象(使用 getUI 方法)。

如果没有多路复用外观,想要增强特定外观的开发人员将需要扩展支持该外观的类。例如,要在不使用多路复用外观的情况下向 Java 外观添加文本到语音的支持,开发人员需要创建一组扩展 Java 外观的类,并将文本添加到- 对新类的语音支持。如果开发人员还想为其他外观(例如 Motif 或 Windows)添加文本到语音的支持,则开发人员还需要创建这些类的子类。

这种方法至少有两个缺点:

  • 首先,每个子类都必须使用本质上是相同代码的副本,这可能会给开发人员带来困难的支持情况。
  • 其次,对最终用户来说更重要的是,一些应用程序开发人员可能会强制使用特定的外观和感觉。使用这种方法时,最终用户甚至无法使用增强的外观。

多路复用观感同时解决了这两个问题,因为它允许组合多种观感。第一个问题(必须使用相当于相同代码的第二个副本)已解决,因为开发人员可以创建专门的外观,然后可以将其与其他外观相结合。

第二个问题(必须强制使用特定外观)已解决,因为专用外观可以与应用程序可能已锁定到位的任何默认外观一起使用。

默认的多路复用外观实现,由 javax.swing.plaf.multi 包中的 MultiLookAndFeel 类表示,称为(毫不奇怪)多路复用外观。


如何使用辅助外观

在 Swing 中使用辅助外观很容易。要指示 Swing 使用多路复用外观,应用程序所要做的就是修改 $JDKHOME/conf/swing.properties 文件以包含 swing.auxiliarylaf 属性的定义。 Swing 将 swing.auxiliarylaf 属性视为以逗号分隔的 LookAndFeel 子类列表,这些子类指定除了默认外观和感觉之外还应使用哪些辅助外观和感觉。如果在 swing.auxiliarylaf 属性中指定了至少一个有效的 LookAndFeel 子类,Swing 会自动使用多路复用外观来加载和支持默认和辅助外观。

例如,假设应用程序使用支持文本到语音反馈的外观,并且还使用添加对散发香水的设备的支持的外观。假设文本到语音的外观命名为 com.myco.TextTalkerLookAndFeel ,添加香水支持的外观命名为 com.smellco.OlfactoryLookAndFeel

要告诉 Swing 使用这两种外观和感觉——并同时使用默认外观和感觉——您的应用程序只需将以下行添加到 $JDKHOME/conf/swing.properties 文件:

    swing.auxiliarylaf=com.myco.TextTalkerLookAndFeel,
        com.smellco.OlfactoryLookAndFeel

此语句告诉 Swing 自动从多路复用外观中获取组件的 UI,而不是直接从默认外观中获取它。生成的多路复用 UI 是一个小委托,它从默认和辅助外观中获取和维护 UI。因此,当在多路复用 UI 对象中调用方法时,多路复用 UI 会在从默认和辅助外观获得的每个 UI 上调用相同的方法。


编写辅助外观的技巧

辅助外观与任何其他外观类似,只是它不必提供默认外观必须提供的完整支持。例如,仅支持文本到语音反馈的辅助外观不需要提供任何绘画代码。此外,它可能不需要支持所有组件——例如,JSeparators 可能会被忽略。

辅助观感往往很简单,因此开发辅助观感比开发视觉观感更容易。开发人员可以专注于提供专门的功能。

因为辅助外观的主要目的是增强默认外观,所以辅助外观往往是非视觉的。然而,由于辅助观感是真正的观感,因此没有什么可以阻止它在显示器上呈现信息。

就像任何其他外观一样,您可以通过编写 javax.swing.LookAndFeel 的子类并创建 javax.swing.plaf 包中定义的 FooUI 类的子类来实现辅助外观。

该做什么和不该做什么

以下段落为开发辅助外观提供了一些一般性建议。

使用installUI方法执行所有初始化,使用uninstallUI方法执行所有清理。

设置组件的外观时会调用 installUIuninstallUI 方法。 installUI 方法使新的 UI 对象有机会在组件及其数据模型上添加监听器。同样,uninstallUI 方法让先前的 UI 对象移除其监听器。

不要扩展视觉外观和感觉。

我们建议您将辅助外观的 UI 类实现为视觉外观的 UI 类的子类。为什么不?因为它们可能会意外继承在组件对象上安装监听或在显示器上呈现组件的代码。因此,您的辅助外观将与默认外观竞争,而不是与之合作。

相反,我们建议辅助外观的 UI 类直接扩展 javax.swing.plaf 包中的抽象 UI 类。通过使用此策略,辅助外观的开发人员可以避免与默认外观竞争。

重写您的 UI 类继承的所有特定于 UI 的方法。

我们建议辅助外观的每个 UI 类覆盖其派生的 javax.swing.plaf UI 类中定义的方法。此建议的原因与不扩展视觉外观的原因类似。例如,ComponentUI 类(所有 UI 类都源自该类)为 update 方法提供了默认实现。如果组件是不透明的,则此默认实现会在显示器上绘制。如果来自非视觉辅助观感的 UI 类不重写此方法,所有不透明组件在屏幕上显示为空白区域!

扩展 UIDefaults

在许多情况下,您可能希望辅助外观“不完整”。也就是说,您可能不需要支持完整的组件集。例如,辅助外观可能会选择提供 ButtonUI 子类但不提供 LabelUI 子类。这个选项是允许的,多路复用的外观和感觉可以优雅地处理这种情况。

然而,默认情况下,当 Swing 询问 UI 对象的外观并且外观不支持该 UI 时,它会发出一条错误消息。此消息可能很烦人,尤其是对于不想支持特定组件的辅助外观开发人员而言。

幸运的是,您可以通过创建 UIDefaults 类的子类并从 LookAndFeel 类的 getDefaults 方法返回它的实例来防止出现此错误消息。例如:

public class MyAuxLookAndFeel extends LookAndFeel {
...
public UIDefaults getDefaults() {
UIDefaults table =
            new MyAuxUIDefaults();
Object[] uiDefaults = {
"ButtonUI", "MyAuxButtonUI",
...
}
table.putDefaults(uiDefaults);
return table;
}
}

class MyAuxUIDefaults extends UIDefaults {
protected void getUIError(String msg) {
//System.err.println
        //   ("An annoying message!");
}
}

在前面的示例中,名为 MyAux 的辅助外观创建了一个覆盖 getUIError 方法的 UIDefaults 子类。 getUIError 方法是当 Swing 在外观中找不到 UI 对象时调用的方法。通过仅在此方法中不执行任何操作,您可以避免错误消息。

检查其他 UI 对象

在极少数情况下,辅助外观的 UI 对象可能会对组件使用的默认 UI 对象感兴趣。在这些情况下,辅助外观的 UI 对象可以通过调用其 getUI 方法从组件获取 UI。返回的 UI 是多路复用外观 UI 类之一的实例(例如,MultiButtonUI)。来自辅助观感的UI对象可以调用返回对象的getUIs方法来获取一个数组,其中包含多路复用UI处理的所有UI对象的完整列表。第一个元素保证是从默认外观创建的 UI。


如何实现多路复用外观

多路复用外观(由 javax.swing.plaf.multi.MultiLookAndFeel 表示)旨在对所有开发人员和用户透明。它应该“正常工作”——并且仅当用户告诉 Swing 使用辅助外观时才使用它。

当使用多路复用外观时,与每个组件关联的 UI 对象的类型取决于当前使用的任何辅助外观是否支持该组件。如果是,则组件的 UI 对象是多路复用 UI 的实例。如果只有默认观感支持该组件,则该组件从默认观感中获取一个 UI 对象,就像没有安装辅助观感一样。

多路复用 UI 对象从默认和辅助外观获取和维护 UI 对象,以下列方式引用这些 UI:

  • 默认外观的 UI 对象始终是最先创建的。之后,按照在 swing.auxiliarylaf 属性中指定的顺序从每个辅助外观创建一个 UI 对象。

  • 当调用从 UI 对象请求信息的方法时,多路复用 UI 对象会在所有 UI 对象上调用该方法,但仅返回 UI 的默认外观结果。例如,在多路复用UI上调用getPreferredSize方法时,UI只返回在默认观感得到的UI上调用getPreferredSize的结果。 getPreferredSize 方法也在 UI 对象上为每个辅助外观调用,但返回值被忽略。

  • 当调用不从 UI 对象请求信息的方法时,多路复用 UI 对象在所有 UI 上调用该方法——在从默认外观获得的 UI 对象上以及在从辅助外观获得的所有 UI 上,还有。例如,在多路复用 UI 上调用installUI 方法会导致多路复用 UI 在从默认外观获得的 UI 和从辅助工厂获得的 UI 上调用 installUI

在所有情况下,首先对从默认外观获得的 UI 对象进行操作,然后按照它们在 swing.auxiliarylaf 属性中指定的顺序对辅助外观进行操作。


如何提供自定义多路复用外观

虽然我们希望多路复用外观的行为足够灵活,不需要替代多路复用外观,但 Swing 允许用户指定要使用的另一种多路复用外观。

为此,用户只需修改 $JDKHOME/conf/swing.properties 文件以包含 swing.plaf.multiplexinglaf 属性的定义。然后 Swing 将 swing.plaf.multiplexinglaf 属性视为支持多路复用的 LookAndFeel 子类。

例如,如果用户具有由 com.myco.SuperMultiLookAndFeel 表示的多路复用外观,比多路复用外观 (javax.swing.plaf.multi.MultiLookAndFeel) 更符合他们的需求,则用户可以在 $JDKHOME/conf/swing.properties 中包含以下行:

swing.plaf.multiplexinglaf = com.myco.SuperMultiLookAndFeel

此语句指示 Swing 使用 com.myco.SuperMultiLookAndFeel 而不是 javax.swing.plaf.multi.MultiLookAndFeel 。但是如果你使用这种声明,要小心,因为辅助外观的供应商很可能已经针对我们的多路复用外观进行了开发和测试。