自定义移动设备外观时,可以创建自定义的移动设备外观类。在某些情况下,也可以编辑移动设备外观类所使用的资源。

编辑移动设备外观类时,可以更改基于状态的交互,实现对新样式的支持,或者在外观中添加或删除子组件。通常可以从现有外观的源代码入手,将其保存为新类。

也可以编辑移动设备外观所使用的资源来更改外观的可视属性,如大小、颜色、渐变和背景。在这种情况下,也可以编辑外观所使用的 FXG 资源。移动设备外观所使用的源 *.fxg 文件位于 spark/skins/mobile/assets 目录下。

并非移动设备外观的所有可视属性都在 *.fxg 文件中定义。例如,Button 外观的背景色由 ButtonSkin 类中的 chromeColor 样式属性定义,而不是在 FXG 资源中定义。在这种情况下,需要编辑外观类来更改背景颜色。

创建移动设备外观类

创建自定义移动设备外观类时,最简单的方法是使用现有移动设备外观类作为基础。然后更改该类,并将其用作自定义外观。

要创建自定义外观类,请执行以下操作:
  1. 在项目中创建目录(例如 customSkins)。此目录的名称是自定义外观的包名称。尽管不需要创建包,但建议您将自定义外观放在单独的包中。

  2. 在新目录中创建自定义外观类。可根据需要为新类命名,例如 CustomButtonSkin.as。

  3. 复制新类所基于的外观类的内容。例如,如果正在使用 ButtonSkin 作为基类,请将 spark.skins.mobile.ButtonSkin 文件的内容复制到新的自定义外观类中。

  4. 编辑新类。例如,至少对 CustomButtonSkin 类进行以下更改:
    • 更改包的位置:
      package customSkins 
      //was: package spark.skins.mobile
    • 在类声明中更改类的名称。它是对新外观所基于的类的扩展(而不是基本外观类):
      public class CustomButtonSkin extends ButtonSkin 
      // was: public class ButtonSkin extends ButtonSkinBase
    • 更改构造函数中的类名称:
      public function CustomButtonSkin() 
      //was: public function ButtonSkin()
  5. 更改自定义外观类。例如,添加对其它状态或新的子组件的支持。此外,外观类本身也定义了一些图形资源,因此可以更改某些资源。

    为使外观类更易于理解,通常可以从自定义外观中删除任何不会被重写的方法。

    以下自定义外观类扩展了 ButtonSkin,并使用自定义逻辑来替换 drawBackground() 方法。它使用径向渐变替换线性渐变来完成背景填充。
    package customSkins {   
        import mx.utils.ColorUtil;
        import spark.skins.mobile.ButtonSkin;
        import flash.display.GradientType;
        import spark.skins.mobile.supportClasses.MobileSkin;
        import flash.geom.Matrix;
    
        public class CustomButtonSkin extends ButtonSkin {
            
            public function CustomButtonSkin() {
                super();
            }
    
            private static var colorMatrix:Matrix = new Matrix();
            private static const CHROME_COLOR_ALPHAS:Array = [1, 1];
            private static const CHROME_COLOR_RATIOS:Array = [0, 127.5];        
            
            override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void {
                super.drawBackground(unscaledWidth, unscaledHeight);        
                
                var chromeColor:uint = getStyle("chromeColor");
                /*
                if (currentState == "down") {
                    graphics.beginFill(chromeColor);
                } else {            
                */      
                var colors:Array = [];
                colorMatrix.createGradientBox(unscaledWidth, unscaledHeight, Math.PI / 2, 0, 0);
                colors[0] = ColorUtil.adjustBrightness2(chromeColor, 70);
                colors[1] = chromeColor;    
                graphics.beginGradientFill(GradientType.RADIAL, colors, CHROME_COLOR_ALPHAS, CHROME_COLOR_RATIOS, colorMatrix);
                // }            
                graphics.drawRoundRect(layoutBorderSize, layoutBorderSize, 
                    unscaledWidth - (layoutBorderSize * 2), 
                    unscaledHeight - (layoutBorderSize * 2), 
                    layoutCornerEllipseSize, layoutCornerEllipseSize);
                graphics.endFill();
            }
            
        }
    }
  6. 在应用程序中,通过应用自定义移动设备外观中介绍的一种方法应用自定义外观。下面的示例使用组件标签上的 skinClass 属性来应用 customSkins.CustomButtonSkin 外观。

    <?xml version="1.0" encoding="utf-8"?>
    <!-- mobile_skins/views/CustomButtonSkinView.mxml -->
    <s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
        xmlns:s="library://ns.adobe.com/flex/spark" title="Home">
        <fx:Declarations>
            <!-- Place non-visual elements (e.g., services, value objects) here -->
        </fx:Declarations>
        
        <s:Button label="Click Me" skinClass="customSkins.CustomButtonSkin"/>
        
    </s:View>

移动设备外观的生命周期方法

创建自定义外观类时,应熟悉以下 UIComponent 方法。这些继承的、受保护的方法将定义外观的子代和成员,并帮助外观与显示列表中的其它组件进行交互。
  • createChildren() — 创建外观所需的任何子图形或文本对象。

  • commitProperties() — 必要时将组件数据复制到外观中。

  • measure() — 尽可能有效地测量外观,并将结果存储在外观的 measuredWidth  measuredHeight 属性中。

  • updateDisplayList() — 设置图形和文本的位置和大小。执行任何所需的 ActionScript 绘制。此方法对外观调用 drawBackground()  layoutContents() 方法。

有关这些方法使用方法的更多信息,请参阅 Implementing the component

常用的移动设备外观自定义方法

许多移动设备外观都实现以下方法:
  • layoutContents() — 确定外观子代(例如投影和标签)的位置。移动设备外观类不支持 HorizontalLayout 和 VerticalLayout 等 Spark 布局。可以在 layoutContents() 等方法中手动布置外观的子代。

  • drawBackground() — 呈示外观的背景。通常情况下,其用途包括绘制 chromeColorbackgroundColor  contentBackgroundColor 样式,具体取决于外观的形状。它还可以用于着色,例如使用 applyColorTransform() 方法。

  • commitCurrentState() — 定义移动设备外观的状态行为。可以通过编辑此方法,来添加或删除支持的状态,或者更改现有状态的行为。当状态改变时调用此方法。大多数外观类都会重写此方法。有关更多信息,请参阅移动设备外观状态

创建自定义 FXG 资源

移动设备外观的大多数可视资源都使用 FXG 进行定义。FXG 是一种声明性语法,用于定义静态图形。可以使用图形工具(如 Adobe Fireworks、Adobe Illustrator 或 Adobe Catalyst)导出 FXG 文档。然后可以在移动设备外观中使用该 FXG 文档。也可以在文本编辑器中创建 FXT 文档,但从头编写复杂的图形非常困难。

移动设备外观通常使用 FXG 文件来定义外观的状态。例如,CheckBoxSkin 类使用以下 FXG 文件来定义其方框和复选标记符号的外观:
  • CheckBox_down.fxg

  • CheckBox_downSymbol.fxg

  • CheckBox_downSymbolSelected.fxg

  • CheckBox_up.fxg

  • CheckBox_upSymbol.fxg

  • CheckBox_upSymbolSelected.fxg

如果在图形编辑器中打开这些文件,将显示如下内容:

复选框状态(down、downSymbol、downSymbolSelected、up、upSymbol 和 upSymbolSelected)

 

适用于多种分辨率的 FXG 文件

大多数移动设备外观都有三组 FXG 图形文件,每一组对应一个默认目标分辨率。例如,所有六个 CheckBoxSkin 类的不同版本位于 spark/skins/mobile160、spark/skins/mobile240 和 spark/skins/mobile320 目录中。

创建自定义外观时,可以执行以下操作之一:
  • 使用一个默认外观作为基础(分辨率通常为 160 DPI)。添加外观缩放逻辑,以通过设置 Application 对象的 applicationDPI 属性来根据运行应用程序的设备缩放自定义外观。

  • 创建三种版本的自定义外观(160、240 和 320 DPI)以优化显示效果。

某些移动设备外观为其图像资源使用一组 FXG 文件,而不具有特定于 DPI 的图形。这些资源存储在 spark/skins/mobile/assets 目录下。例如,ViewMenuItem 外观和 TabbedViewNavigator 按钮栏外观不具有特定于 DPI 的版本,因此其所有 FXG 资源都存储在此目录下。

自定义 FXG 文件

可以打开现有的 FXG 文件并对其进行自定义,或者在图形编辑器(例如 Adobe Illustrator)中创建并导出 FXG 文件。编辑 FXG 文件后,将其应用到外观类。

要通过修改 FXG 文件来创建自定义外观,请执行以下操作:
  1. 创建自定义外观类并将其放在 customSkins 目录下,如创建移动设备外观类中所述。

  2. 在 customSkins 目录下创建一个子目录,例如 assets。创建子目录是可选步骤,但有助于组织 FXG 文件和外观类。

  3. 在 assets 目录下创建一个文件,并将现有 FXG 文件的内容复制到该文件中。例如,创建名为 CustomCheckBox_upSymbol.fxg 的文件。将 spark/skins/mobile160/assets/CheckBox_upSymbol.fxg 内容复制到新建的 CustomCheckBox_upSymbol.fxg 文件中。

  4. 更改新的 FXG 文件。例如,使用以渐变项填充的“X”号替换复选标记的绘制逻辑:
    <?xml version='1.0' encoding='UTF-8'?>
    <!-- mobile_skins/customSkins/assets/CustomCheckBox_upSymbol.fxg -->
    <Graphic xmlns="http://ns.adobe.com/fxg/2008" version="2.0"
        viewWidth="32" viewHeight="32">
    
        <!-- Main Outer Border -->
        <Rect x="1" y="1" height="30" width="30" radiusX="2" radiusY="2">
            <stroke>
                <SolidColorStroke weight="1" color="#282828"/>
            </stroke>
        </Rect>     
    
        <!-- Replace check mark with an "x" -->
        <Group x="2" y="2">
            <Line xFrom="3" yFrom="3" xTo="25" yTo="25">
                <stroke>
                    <LinearGradientStroke caps="none" weight="8" joints="miter" miterLimit="4">
                        <GradientEntry color="#FF0033"/>
                        <GradientEntry color="#0066FF"/>
                    </LinearGradientStroke>
                </stroke>
            </Line>
            <Line xFrom="25" yFrom="3" xTo="3" yTo="25">
                <stroke>
                <stroke>
                    <LinearGradientStroke caps="none" weight="8" joints="miter" miterLimit="4">
                        <GradientEntry color="#FF0033"/>
                        <GradientEntry color="#0066FF"/>
                    </LinearGradientStroke>
                </stroke>
                </stroke>
            </Line>
        </Group>
    </Graphic>
  5. 在自定义外观类中,导入新 FXG 类,并将其应用到某个属性。例如,在 CustomCheckBox 类中:
    1. 导入新的 FXG 文件:
      //import spark.skins.mobile.assets.CheckBox_upSymbol; 
      import customSkins.assets.CustomCheckBox_upSymbol;
    2. 将新资源添加到自定义外观类中。例如,更改 upSymbolIconClass 属性的值以指向新的 FXG 资源:
      upSymbolIconClass = CustomCheckBox_upSymbol;

完整的自定义外观类如下所示:

// mobile_skins/customSkins/CustomCheckBoxSkin.as 
package customSkins {   
    import spark.skins.mobile.CheckBoxSkin;
    import customSkins.assets.CustomCheckBox_upSymbol; 
    
    public class CustomCheckBoxSkin extends CheckBoxSkin { 
        public function CustomCheckBoxSkin() { 
            super(); 
            upSymbolIconClass = CustomCheckBox_upSymbol; // was CheckBox_upSymbol 
        } 
    } 
}

有关处理和优化外观 FXG 资源的信息,请参阅 Optimizing FXG

相关文章: