About

<#TEMPLATE_INCLUDE_NINEPAGE_ABOUTME#>
  • Feb

    3

    升级指南:返回类型 API 更改:

    Drawers 现在要求 content 与 drawers 都为 Feathers 组件,在以前的旧版本中可以使用 starling.display.Sprite 对象,新版中不再被允许了,如果想用一个对象代替以往通用的 Starling  Sprite 对象,可以考虑使用 LayoutGroup 作为替代品。

    INativeFocusOwner 实现类的 nativeFocus 属性现在返回一个 Object  对象,以允许像 flash.text.StageText 这样的对象接收焦点。以前的旧版本中返回的是 flash.display.InteractiveObject 对象。

    Feb

    3

    升级指南:关于尺寸测量—— 用 saveMeasurements() 方法代替 setSizeInternal() 方法:

    自定义组件时应该使用新的 saveMeasurements() 方法代替旧的 setSizeInternal() 方法,旧方法已被视为弃用的(不再被推荐)。

    新方法除了有更直观的名字外,saveMeasurements() 允许组件动态设置最小尺寸(最小宽度和高度):

    this.saveMeasurements(newWidth, newHeight, newMinWidth, newMinHeight);

    如果想和旧版本的行为相似,可以将最小宽与高设为 0:

    this.saveMeasurements(newWidth, newHeight, 00);

    还可以将最小尺寸与常规尺寸相同:

    this.saveMeasurements(newWidth, newHeight, newWidth, newHeight);

    如果没有为 Feathers 组件明确的指定最小尺寸,Feathers 会自动计算出一个最小尺寸。

    关于 saveMeasurements() 更多的其它备注资料查看:Feathers3 “组件生命周期”一些备注》。

    Feb

    3

    升级指南:皮肤方面的更改(4)—— 删除了 StandardIcons.listDrillDownAccessoryTexture:

    在以前的版本中,此静态属性用于返回项渲染器 accessorySourceFunction 中的特定图标(冒似下拉图标)。在 Feathers 3.0 中,用户应将 DefaultListItemRenderer.ALTERNATE_STYLE_NAME_DRILL_DOWN 常量添加到项渲染器的 styleNameList 中,主题会自动检测此样式名称,并为项渲染器传入一个特定的图标。DefaultGroupedListItemRenderer  也同样定义了一个ALTERNATE_STYLE_NAME_DRILL_DOWN 常量。

    如果列表中的一些项渲染器不需要特定的图标,使用 setItemRendererFactoryWithID() 和 factoryIDFunction 为 List 或 GroupedList 传入多个项渲染器

    list.itemRendererFactory = function():IListItemRenderer
    {
        var itemRenderer:DefaultListItemRenderer = new DefaultListItemRenderer();
        itemRenderer.styleNameList.add( DefaultListItemRenderer.ALTERNATE_STYLE_NAME_DRILL_DOWN );
        return itemRenderer;
    };

    如果所有的项都有特定的图标,则可以改用 customItemRendererStyleName 属性:

    list.customItemRendererStyleName = DefaultListItemRenderer.ALTERNATE_STYLE_NAME_DRILL_DOWN;

    Feb

    3

    升级指南:皮肤方面的更改(3)—— 通过文本渲染器设置字体样式:

    现在当 Feathers 父组件的状态更改时,可以通过“文本渲染器”(Text renderers)设置字体样式了。

    在以前的版本中,Button类在编译期并未进行严格类型检查,因以前的版本中多状态的属性设置大多是通过 Object  动态类管理的(类似 defaultLabelProperties、downLabelProperties这些属性);现在新版本的方法在主题文件外自定义文本字体样式时将更加严谨,也更容易。 

    button.labelFactory = function():ITextRenderer
    {
        var textRenderer:BitmapFontTextRenderer = new BitmapFontTextRenderer();
        textRenderer.styleProvider = null; // 避免与主题文件冲突
        textRenderer.textFormat = new BitmapFontTextFormat( "My Font", BitmapFont.NATIVE_SIZE, 0xffffff );
        return textRenderer;
    };

    如在上面的代码中,在 Button 的 labelFactory 工厂中,创建了一个 BitmapFontTextRenderer 对象,然后在通过 textFormat 属性设置字体样式。

    在 BitmapFontTextRenderer 这个文本渲染器对象上,可以调用 setTextFormatForState() 方法为按钮的不同状态设置不同的字体样式,在同一个 labelFactory 工厂函数体内就可以完成这些设置:

    textRenderer.setTextFormatForState( ButtonState.DOWN,
        new BitmapFontTextFormat( "My Font", BitmapFont.NATIVE_SIZE, 0xffcc00 ) );

    textRenderer.setTextFormatForState( ButtonState.DISABLED,
        new BitmapFontTextFormat( "My Font", BitmapFont.NATIVE_SIZE, 0x999999 ) );

    使用不同的文本渲染器显示文本,基本用法大多是相同的,但不同的文本渲染器都有自己设置字体样式的方法。比如一个 TextBlockTextRenderer 文本渲染器设置字体样式,需要将字体样式传递给 elementFormat 属性,然后调用 setElementFormatForState() 方法为不同的状态设置字体样式。

    如果希望将样式有关的代码都集中在主题文件中,可以为指定的文本渲染器类创建样式名称。在 Button 对象上,传递样式名称给 customLabelStyleName 属性:

    button.customLabelStyleName = "my-custom-text-renderer";

    在主题文件中,为 BitmapFontTextRenderer 类添加一个新的样式提供程序:

    getStyleProviderForClass( BitmapFontTextRenderer )
        .setFunctionForStyleName( "my-custom-text-renderer", setCustomTextRendererStyles );

    和前面在主题文件外设置类似,为不同的状态设置不同的字体样式:

    function setCustomTextRendererStyles( textRenderer: BitmapFontTextRenderer ):void
    {
        textRenderer.textFormat = new BitmapFontTextFormat( "My Font", BitmapFont.NATIVE_SIZE, 0xffffff );
        textRenderer.setElementFormatForState( ButtonState.DOWN,
            new BitmapFontTextFormat( "My Font", BitmapFont.NATIVE_SIZE, 0xffcc00 ) );
        textRenderer.setElementFormatForState( ButtonState.DISABLED,
            new BitmapFontTextFormat( "My Font", BitmapFont.NATIVE_SIZE, 0x999999 ) );
    }

    Feb

    3

    升级指南:皮肤方面的更改(2)—— 提供了新的 ImageSkin 类:

    feathers.skin.ImageSkin 为 Feathers 带有多个状态的组件的皮肤设置提供了一个优化方案,类似 Button、TextInput 等带有多个状态的组件。

    在以的版本中,设置这些组件皮肤最简单方法是为每个状态创建单独的显示对象,例如一个按钮有默认状态、悬停状态、按下状态、禁用状态,用户需要通过四个纹理创建四个单独的 Image 对象,用于皮肤设置。

    从优化的角度考虑,最好使用单个 Image 对象,并简单地将它与所有四个纹理重复使用。以前的版本中可以使用 stateToSkinFunction 属性,但它并不是很直观,开发人员更喜欢为每个状态指定一个显示对象。ImageSkin 类利用了 Starling 2.0 的新功能,使得使用多个纹理变得容易。

    以按钮为例,如何使用 ImageSkin 类的样例代码:

    var skin:ImageSkin = new ImageSkin( defaultTexture );
    skin.setTextureForState( ButtonState.DOWN, downTexture );
    skin.setTextureForState( ButtonState.DISABLED, disabledTexture );
    skin.scale9Grid = SCALE_9_GRID;
    button.defaultSkin = skin;

    每个状态可以显示不同的纹理,并且 ImageSkin 会自动检测按钮的状态的更改并显示对应的纹理。如果没有为某个状态指定对应的纹理,就会使用默认纹理。

    ImageSkin 是 starling.display.Image 的子类,所以像 scale9Grid 和 tileGrid 这些属性都是可用的。

    ImageSkin 除了可以用于设置背景皮肤外,还可以用于设置按钮的 icon,文本输入框(text inputs)和项渲染器(item renderers)等。

    Feb

    2

    升级指南:皮肤方面的更改(1)——删除了 Scale9Image,Scale3Image 和 TiledImage:

    Starling 2.0 开始已经原生支持这些功能了,所以 Feathers 3.0 中已经没有了 Scale9Image,Scale3Image 和 TiledImage 这些组件。

    1、Scale9Image 的替代方案:是创建一个 Image 对象,然后设置它的 scale9Grid 属性。样例代码:

    var image:Image = new Image( texture );
    image.scale9Grid = new Rectangle( 2236 );
    this.addChild( image );

    2、Scale3Image 的替代方案:同样是创建一个 Image 对象,然后设置它的 scale9Grid 属性。当水平缩放的时候,将网格的高度设为纹理的完整高度;当垂直缩放的时候,将网格的宽度设为纹理的完整宽度。水平缩放的样例代码:

    var image:Image = new Image( texture );
    image.scale9Grid = new Rectangle( 203, texture.frameHeight );
    this.addChild( image );

    3、TiledImage 的替代方案:创建一个 Image 对象,设置 tileGrid 属性为一个空的 Rectangle 对象。样例代码:

    var image:Image = new Image( texture );
    image.tileGrid = new Rectangle();
    this.addChild( image );

    Feb

    2

    如果想要修改Feathers 示例主题的原始设计,可以下载源文件。每一个主题文件是一个 FLA 文档,使用 Animate CC 打开(Adobe 公司已经抛弃了 Flash CC这个名称,已经改名为 Animate CC)。

    可以修改原始的矢量稿,然后重新导出 sprite sheet。可能还需要下载其它用于创建纹理集图像的打包 PNG 的工具,类似 Texture Packer

    Feb

    2

    Feathers 2.0 开始包含了一个全新的皮肤架构。用于旧主题扩展的 DisplayListWatcher 类仍然存在,可以在一定时间内继续使用它(3.0 开始冒似不存在了……)。如果开发者想利用新的样式提供者带来的好处,那么不得不对主题文件做一些修改。

    扩展新类:StyleNameFunctionTheme

    原生旧主题是扩展f eathers.core.DisplayListWatcher 类:

    // legacy
    public class CustomTheme extends DisplayListWatcher

    要创建一个更符合“现代化”的主题,请扩展 feathers.themes.StyleNameFunctionTheme:

    // modern
    public class CustomTheme extends StyleNameFunctionTheme

    这么改完之后如果直接编译可能会报错,需要对主题文件稍作修改。

    替换 setInitializerForClass() 方法:

    新的 StyleNameFunctionTheme 对象依旧会调用函数设置组件的属性,类似旧主题,开发者依旧可以使用字符串作为样式名称区分相同类型的组件却不同的外观,但 API 有些改变。

    旧主题中,开发者使用 setInitializerForClass() 方法,并传入一个组件类和一个设置组件皮肤用的函数,有时候还会传入第三个参数用于区分相同组件的不同外观。

    // legacy
    this.setInitializerForClass( Button, setButtonStyles );
    this.setInitializerForClass( Button, setCustomButtonStyles, "my-custom-button" );

    在新主题中,开发者首先需要获得一个组件类的全局样式提供者引用,然后才能设置它的默认外观与特定的外观:

    // modern
    this.getStyleProviderForClass(Button).defaultStyleFunction = setButtonStyles;
    this.getStyleProviderForClass(Button).setFunctionForStyleName( "my-custom-button", setCustomButtonStyles );

    一个“快速偷懒”的方法:

    如果旧主题文件中有大量的 setInitializerForClass() 方法需要替换,这可能会是耗时和繁琐的。 如果需要快速的升级主题,并且懒的清理和整理旧代码内容,可以将以下函数复制到新主题类中:

    public function setInitializerForClass(type:Class, styleFunction:Function, styleName:String = null):void
    {
        var styleProvider:StyleNameFunctionStyleProvider = this.getStyleProviderForClass(type);
        if(styleName)
        {
            styleProvider.setFunctionForStyleName(styleName, styleFunction);
        }
        else
        {
            styleProvider.defaultStyleFunction = styleFunction;
        }
    }

    这样它的签名方法就与旧主题文件的 setInitializerForClass() 一样,但使用的却是新主题中的全局样式提供程序。

    替换 setInitializerForClassAndSubclasses() 方法:

    这个旧方法没替代的新方法。这个旧方法主要是为了解决旧有皮肤机制中的一些限制:比如默认一个子类在没有设置皮肤的情况下,不会使用父类的皮肤。而新的主题机制中默认子组件如果没有提供皮肤,会使用和父类相同的皮肤(除非子类自己不要父类的皮肤)。所以这个旧方法已经没有存在的必要了。

    旧主题开发时编写的代码:

    // legacy
    this.setInitializerForClassAndSubclasses( Scroller, setScrollerStyles );

    而在新主题开发时,应该直接调用对应的函数,比如开发者对 Scroller 类设置了一些通用的样式,就可以在 List 组件样式设置的函数中直接调用对应用的函数(如上面的 setScrollerStyles() 函数):

    // modern
    protected function setListStyles( list:List ):void
    {
        this.setScrollerStyles( list );

        // set other styles here
    }

    现在当 List 类(其它的任何子类)实例化设置样式的时候,setScrollerStyles() 也将会被调用。

    替换 exclude() 方法:

    在旧的主题机制下,如果希望一个组件不接受默认的皮肤设置,将该组件传递给 DisplayListWatcher 对象的 exclude() 方法:

    // legacy
    theme.exclude( button );

    在新的主题机制,直接清除样式提供程序就可以:

    // modern
    button.styleProvider = null;

    确保在组件初始化之前清除样式提供程序(皮肤设置是在初始化的步骤中)。默认情况下,当组件被添加到舞台时会执行初始化。

    将“name”替换为“style name”:

    为了解决开发人员使用 getChildByName() 的一些问题,Feathers 不再使用 name 和 nameList 属性来表示主题应该给组件一个可替代的视觉外观,而是使用 styleNameList 或 styleName 属性:

    // 旧
    button.nameList.add( "my-custom-button" );
    // or
    button.name = "my-custom-button";

    // 新
    button.styleNameList.add( "my-custom-button" );
    // or
    button.styleName = "my-custom-button";

    nameList 依旧存在于Feathers 2.0 和 2.1 版本,但从 2.2 开始它不存在了。在仍然存在的版本中,它只是映射到 styleNameList 属性,以便旧代码继续工作。

    name 属性不再用于对 Feathers 的样式化,它不会被映射到 styleName 属性,这是为了防止开发者在使用 getChildByName() 时出现潜在问题。

    一些可替换的样式名称(被定义为常量)也已经被改名,例如包含 NAME 常量名的已经改变为 STYLE_NAME 常量名,例如:Button.ALTERNATE_NAME_DANGER 被改为了 Button.ALTERNATE_STYLE_NAME_DANGER 。类似的 Slider.DEFAULT_CHILD_NAME_THUMB 被改为了 Slider.DEFAULT_CHILD_STYLE_NAME_THUMB。

    对应的,一些父组件上对子组件公开的 API 也有类似的改变,Name 替换为了 StyleName,例如:customThumbName 属性被改为了 customThumbStyleName 属性。

    Feb

    2

    在《创建自定义主题》资料中已经有提到主题文件中有两种方法管理资源文件(类似纹理图像、位图字体等):一种是静态嵌入资源,另一种是运行时动态加载资源。

    静态嵌入资源:

    这种方法是将主题相关的资源文件编译到最终的 SWF 文件(或 SWC 文件中),将所有资源都放在了一个位置。这种方式的资源都是在启动应用时预加载的,可以直接实例化并使用。

    Feathers 的初学者最简单的方法使用示例主题,只需要将 SWC 文件放在项目的库链接中,在需要的时候实例化它:

    new MetalWorksMobileTheme();

    但是这种方法需要更多的内存,因为 SWF 是一个压缩文件, Flash 运行时会将整个SWF 解压到内存中,当实例化嵌入式资源时,这些资源会从运行时的 “SWF 的库”复制到内存,所以实际是使用了两倍的内存。

    SWF 的库不同于 Flash IDE 的库:Flash IDE 的库存放的是所有有用或不用的元件,而 SWF 的库是在运行时解压到内存中所有被编译过的资源文件,它同样是以一个“资源库”的概念存在。

    示例主题嵌入了比较小的纹理集,因此对应用程序来说这种影响可能不会太大,但当开发一个大型项目时用到的资源量如果非常巨大,那么就需要很多内存(因为内存是使用量是翻倍的)。这种静态嵌入资源的方式一般只适合初学者为方便开发、或小资源项目、或快速测试功能与模块时使用。

    运行时动态加载资源:

    通过 Starling 的 AssetManager 类运行时动态加载资源,可以将资源与代码分离,资源文件可以是通过网络 URL 加载、或本地文件系统、或打包到 AIR 程序内的文件。这种方式使用内存较小,因为它只会在内存中出现一次。但编写的代码量稍稍多一些,复杂一些。

    首先需要指定资源的位置,如果是 AIR 程序,可以将资源文件一起打包,如果使用 MetalWorksMobileThemeWithAssetManager 类,需要以下两个文件:

    images/metalworks.xml
    images/metalworks.png

    如下的样例代码中,需要告知主题文件,images 文件夹放在 File.applicationDirectory 中:

    var theme:MetalWorksMobileThemeWithAssetManager =
        new MetalWorksMobileThemeWithAssetManager( File.applicationDirectory.url );

    资源文件并不是必须放在 File.applicationDirectory 中的,开发者也可以将资源文件放在任意子目录中。Flash Builder 和其它 IDE都可以指定打包文件的位置(既便是纯粹的命令行 ADT 打包也可以指定)。

    然后为主题对象添加一个侦听器:

    theme.addEventListener( Event.COMPLETE, theme_completeHandler );

    当资源管理器完成加载时,主题会派发这个事件,表现主题已经准备好为组件提供皮肤了(包括加载的纹理)。换句话说,开发者应该一直等待这个事件派发,然后才可以实例化组件显示用户界面。

    事件的侦听器可能如下所示:

    private function theme_completeHandler( event:Event ):void
    {
        // the theme is ready!

        this.button = new Button();
        this.button.label = "Click Me";
        this.addChild( button );
    }

    其它主题也一样会有类似的要资源文件需求。只需要开发者记住将主题文件当成指向的目录,如在上面的 MetalWorksMobileThemeWithAssetManager 主题例子中,指向的目录引用并不是 images,而是它的父目录。

    在未末的 Feathers 版本中,这个主题可能会需要 images 目录旁边的额外目录中的资源,因此希望保持其灵活性。例如,MinimalMobileThemeWithAssetManager 需要以下文件: 

    images/minimal.xml
    images/minimal.png
    fonts/pf_ronda_seven.fnt

    从上面的层级可以看到,图像与字体并不在同一个目录中, 图像和字体的父目录是该主题文件的真正“根”目录。

    使用自定义资源管理器加载资源:

    默认情况下,主题会创建自己的资源管理器对象(AssetManager 对象)。如果开发者想加载主题文件所不知道的额外的文件,可以选择将自己创建的资源管理器传递给示例主题的构造函数: 

    var assets:AssetManager = new AssetManager();
    assets.enqueue( File.applicationDirectory.resolvePath( "./images/custom-asset.png" ) );

    var theme:MetalWorksMobileThemeWithAssetManager =
        new MetalWorksMobileThemeWithAssetManager( File.applicationDirectory.url, assets );

    theme.addEventListener( Event.COMPLETE, theme_completeHandler );

    在传递到主题的构造函数前,添加任何额外需要加载的资源文件。

    传递给主题构造函数的 AssetManager 对象决对不要调用 loadQueue() 方法,因为主题对象会自动调用这个方法(Starling 的 AssetManager 对象不会派发 Event.COMPLETE 事件给主题),当所有资源加载完成时主题会派发 Event.COMPLETE 事件。开发者如果需要知道何时资源文件被完全加载,只需在主题对象上侦听Event.COMPLETE 事件。

    相关链接

    https://feathersui.com/help/theme-assets.html

    Feb

    2

    样式提供程序(Style providers,就是指用于设置样式的方法或函数这样的片段代码,引用这段代码程序的变量也可以称为“样式提供者”)是主题文件的基础模块,它可以为每个组件进行单独的设置样式,也可以用于全局性的样式设置,在组件实例化的时候会自动设置皮肤。

    一般来说,大多数 Feathers 用户可能用不着看这篇贴子,基本上都是通过主题文件管理皮肤,但也可以深入的了解一样它背后的原理。

    为同一类型的组件设置不同的皮肤样式:

    如平时我们有多个按钮,需要有相同的外观皮肤,开发者可能就会寻找一种通用的方法,避免一次又一次的复制和粘贴皮肤相关的代码。所以 Feathers 组件支持一种被称为样式提供程序的东西,用于组件实列化的时候自动对组件设置皮肤。

    创建一个样式提供程序者(FunctionStyleProvider 对象):

    function skinButton( button:Button ):void
    {
        button.defaultSkin = new Quad( 20060, 0xcccccc );
        button.downSkin = new Quad( 20060, 0x999999 );
    }
    var customButtonStyles:FunctionStyleProvider = new FunctionStyleProvider( skinButton );

    这段代码中创建了一个方法,接受一个需要被样式化的组件。customButtonStyles 就可以被称为样式提供者,它是一个 FunctionStyleProvider 对象;而 skinButton() 方法被称为样式提供程序,是一个函数或方法。

    通知组件去使用一个样式提供者,就像设置组件的 styleProvider 属性一样简单:

    var button1:Button = new Button();
    button1.label = "Cancel";
    button1.styleProvider = customButtonStyles;
    this.addChild( button1 );

    var button2:Button = new Button();
    button2.label = "Delete";
    button2.styleProvider = customButtonStyles;
    button2.y = 100;
    this.addChild( button2 );

    这段代码中有两个按钮,使用同一段样式提供程序,所以会有相同的皮肤。

    自动为相同类型的组件设置皮肤:

    在上面的代码,创建了一个 FunctionStyleProvider 对象作为局部变量或实例变量,并简单地在两个按钮上设置 styleProvider 属性。这在只有一个类的情况下用着还行,但通常一个组件会出现在很多个其它类文件中,而且这么一个一个的高就比较麻烦了。所以我们需要一种全局性的样式提供者(global style provider)。

    每一个组件类(比如  (Button、Slider、List 等)都会有一个静态 globalStyleProvider 属性,在下面的示例中,我们将为所有按钮设置全局样式提供程序:

    function skinButton( button:Button ):void
    {
        button.defaultSkin = new Quad( 20060, 0xcccccc );
        button.downSkin = new Quad( 20060, 0x999999 );
    }
    Button.globalStyleProvider = new FunctionStyleProvider( skinButton );

    和前面一样,创建了一个 FunctionStyleProvider 对象,但赋值给了 Button 的静态 globalStyleProvider 属性。

    现在,创建按钮时,就不再需要为每个按钮单独设置 styleProvider 属性了:

    var button1:Button = new Button();
    button1.label = "Cancel";
    this.addChild( button1 );

    var button2:Button = new Button();
    button2.label = "Delete";
    button2.y = 100;
    this.addChild( button2 );

    现在当按钮创建的时候,Feathers 会自动设置它的 styleProvider 属性为 Button.globalStyleProvider 的值。

    trace( button1.styleProvider == Button.globalStyleProvider )//输出 true

    忽略单个组件的全局样式:

    但有时候会有例外的情况,开发者并不希望使用全局样式提供程序。用自己的皮肤替换默认皮肤的最简单的方法是清除按钮的 styleProvider 属性,并从头开始:

    var button1:Button = new Button();
    button1.label = "Click Me";

    //不使用默认样式提供程序
    button1.styleProvider = null;

    //从零开始设置新的皮肤
    button1.defaultSkin = new Quad( 20060, 0xff0000 );
    button1.downSkin = new Quad( 20060, 0x000000 );
    button1.fontStyles = new TextFormat( "_sans"36, 0xffffff );
    button1.padding = 10;

    this.addChild( button1 );

    现在,上面例子的按钮不再有默认的样式提供程序,所以新创建的皮肤就不会被默认样式提供程序覆盖了。styleProvider 也可以不设为 null ,而是直接传入一个新的自定义的 FunctionStyleProvider 对象。

    同一类型的组件使用多种不同的全局样式:

    如果有许多个同类型的按钮组件,希望其中一个有不同的外观,可以为它们提供不同的样式提供程序,如:

    function skinNormalButton( button:Button ):void
    {
        button.defaultSkin = new Quad( 20060, 0xcccccc );
        button.downSkin = new Quad( 20060, 0x999999 );
    }
    function skinWarningButton( button:Button ):void
    {
        button.defaultSkin = new Quad( 20060, 0xff0000 );
        button.downSkin = new Quad( 20060, 0xcc0000 );
    }

    var button1:Button = new Button();
    button1.label = "Cancel";
    button1.styleProvider = new FunctionStyleProvider( skinNormalButton );
    this.addChild( button1 );

    var button2:Button = new Button();
    button2.label = "Delete";
    button2.styleProvider = new FunctionStyleProvider( skinWarningButton );
    button2.y = 100;
    this.addChild( button2 );

    但和前面说的一样,剩下的相同外观的按钮每个设置一次,依旧会很麻烦。如果能够像前面那样直接使用一个 Button.globalStyleProvider 全局样式提供者就可以方便很多。幸运的是,FunctionStyleProvider 并不是唯一可用的样式提供者,还有一个名为 StyleNameFunctionStyleProvider 类,允许为组件定义多个外观相关的样式提供程序。如:

    function skinNormalButton( button:Button ):void
    {
        button.defaultSkin = new Quad( 20060, 0xcccccc );
        button.downSkin = new Quad( 20060, 0x999999 );
    }
    function skinWarningButton( button:Button ):void
    {
        button.defaultSkin = new Quad( 20060, 0xff0000 );
        button.downSkin = new Quad( 20060, 0xcc0000 );
    }

    var buttonStyleProvider:StyleNameFunctionStyleProvider = new StyleNameFunctionStyleProvider();
    buttonStyleProvider.defaultStyleFunction = skinNormalButton;
    buttonStyleProvider.setFunctionForStyleName( "warning-button", skinWarningButton );
    Button.globalStyleProvider = buttonStyleProvider;

    在上面的代码中,skinNormalButton 函数被用于 Button 的默认皮肤设置,然后调用了 setFunctionForStyleName() 方法,将 skinWarningButton() 函数关联到一个名为 “warning-button” 的样式名称上。

    样式名称是一个字符串,用来区分同一个类型的组件不同的外观。将样式名称添加到组件的 styleNameList 属性中,如:

    var button1:Button = new Button();
    button1.label = "Cancel";
    this.addChild( button1 );

    var button2:Button = new Button();
    button2.label = "Delete";
    button2.styleNameList.add( "warning-button" );
    button2.y = 100;
    this.addChild( button2 );

    如上代码中,将 “warning-button” 样式名称添加到第二个按钮的 styleNameList 对象中,StyleNameFunctionStyleProvider 对象会根据这个样式名称来确定第二个按钮的样式提供程序是 setWarningButtonStyles() 方法,而不是 setNormalButtonStyles() 方法。

    为了在多个文件中输入字符串时出现“输入性的”(手误)错误时可以被编译器捕获,推荐样式名称最好定义为静态常量。

    样式提供程序与主题文件:

    样式提供程序是主题文件的基础模块,主题文件允许您将所有的全局样式代码合并到一个主题文件类中。 通常,当应用程序首次启动时,主题会被用于实例化。

    Feathers UI 库中提供的示例主题使用了 StyleNameFunctionStyleProvider 对象,为所有组件提供了样式提供程序,一些函数被传递到 defaultStyleFunction 属性,以便在组件没有任何样式名称时提供默认样式。一些函数被传递给 setFunctionForStyleName() 方法,与样式名关联起来。关于示例主题相关的资料参考《“扩展示例主题”一些备注

    词汇表:

    样式提供程序(style provider provides):它为组件初始化时提供皮肤化设置。

    全局样式提供者(global style provider):它为同一类型的组件自动化的提供皮肤化设置。在组件初始化之前,如果有需要,可以轻松的将全局样式提供程序替换为自定义的样式提供程序。

    样式名称(style name):通过在组件上设置样式名称,我们可以通知样式提供者为单个组件提供可选的皮肤化行为。

    主题文件(theme):主题允许我们将所有的全局样式代码放在同一个位置(通常只在一个类中,可以在应用程序启动时用于主题实例化)。