Flex 4.5提供的移动增强的皮肤特性,支持触摸交互、性能优良,并且考虑到了内存占用问题。尽管目前市场上有不少性能优异的设备,但典型的Spark皮肤(包括Flex 4引入的默认皮肤)却没有能够在移动设备上得到很好的应用。Adobe为移动优化过的皮肤在设计时就考虑到平衡两个对立的目标:性能优异却又容易创建。虽然MXML皮肤在某些情况下是有用的,但Adobe还是建议遵循以下简单的方针,确保Flex 4.5移动应用程序能够同时满足开发者和最终用户的性能要求。

这是介绍Flex 4.5移动皮肤特性系列文章中的第一篇。

本文将涵盖创建移动增强皮肤的基本知识,包括:

  • Spark主题和Mobile主题的区别
  • MobileSkin基类中包含的性能优化
  • 用FXG替代MXML图形
  • 基于Mobile主题的ButtonSkin类创建自定义的Button皮肤

该系列文章中的余下几篇将会介绍更多高级主题,包括:

  • 基于MobileSkin基类构建新的皮肤
  • 创建空间感知的皮肤,可以适应所有屏幕尺寸
  • 使用CSS媒体查询为每个平台创建自定义的主题

比较Spark和Mobile 主题

Mobile主题基本是Flex 4引入的Spark主题功能集的一个子集。Mobile主题专门针对性能和内存占用问题进行的优化。

MobileSkin 基类

Spark 皮肤通常需要继承 Skin (它扩展了 Group) 或者 SparkSkin (它扩展了 Skin)。Mobile主题里的皮肤使用一个新的基类MobileSkin,它扩展了UIComponent。

状态

MobileSkin对状态的支持是它一项针对移动开发优化过的主要特性。一般情况下,使用状态特性(在MXML或者ActionScript)都会带来额外的内存和性能消耗。而MobileSkin不再用State类及子类,而是手工的在代码里处理状态改变,例如SetProperty 和 AddChild

布局

因为MobileSkin不是一个 Group, 它不能使用 Spark的基于约束的布局。例如 HorizontalLayout, VerticalLayout和BasicLayout 。

MobileSkin的内容在代码中人工的布局。MobileSkin加入了新的生命周期方法layoutContents(),,它在updateDisplayList() 中被调用。这个方法用于放置皮肤的子元素。

FXG 和 MXML 图形

Mobile主题中皮肤的图形由编译过的FXG和用于绘制的ActionScript代码构成。绘制代码仅用于琐碎的图形或者需要支持样式的时候。处于性能优化的考虑,所有其他图形都使用编译过的FXG。MobileSkin基类不选择使用MXML图形,除非他们都包含在一个Group当中。

文本

Mobile主题在默认皮肤中不会使用Flash Text Engine (FTE)或者Text Layout Framework (TLF) 。这主要是基于性能的考虑,并且为了支持原生的文本输入和编辑。

Flex 4.5在mobilecomponents.swc中引入了一个新的StyleableTextField类。它扩展了TextField,并且加入了对样式的支持。它专门用于那些不准备与MXML结合使用的移动ActionScript皮肤和渲染器。

在同时使用StyleableTextField 和 Label (基于 FTE)时,开发者必须嵌入字体两次。Label 使用embedAsCFF=true,而 TextField 和 StyleableTextField 用embedAsCFF=false

Adobe不建议在移动项目中使用RichEditableText组件。请用TextArea组件代替。

MXML 和 ActionScript

由于MobileSkin删除了MXML皮肤的需要主要的申明性特性(状态、布局和MXML图形),Adobe建议用ActionScript编写皮肤。没有这三个特性,用MXML做声明性的标识已经没有什么优势了。

注意: 在Flash Builder中,可以指定库和主题的SWCs的代码或者编译过的ActionScript皮肤,由设计视同渲染出MXML皮肤。但设计视图不能通过指定代码渲染ActionScript皮肤。

不支持的全局样式

由于上文提到限制,Mobile主题删除了一些样式,包括:

  • rollOverColor – 不支持,因为基础可触屏的界面是目前主要环境。
  • borderAlpha , borderColor , cornerRadius – 不支持,因为这些参数是编译过的FXG的属性,他们不会在运行时发生变化
  • dropShadowVisible – 不支持,为性能考虑,尽量减少过滤器的使用。
  • Flash Builder 会根据当前选择的主题,在MXML和CSS编辑器中正确的显示或者隐藏样式属性

Mobile中避免使用的组件

由于各种原因,某些Spark组件在Mobile主题中没有皮肤。例如,有些组件在Mobile UI下没有什么作用,或者它根本就不是 Flex 4.5 版本的主要目标,可能在下一个发行版中专门为移动平台进行优化。这些组件包括:

  • ComboBox and DropDownList
  • NumericStepper
  • ToggleButton
  • VideoDisplay and VideoPlayer
  • VSlider
  • Panel
  • TabBar (the component used in TabbedViewNavigator is actually a ButtonBar)
  • TitleWindow

Button皮肤教程概述

开始使用皮肤特性的最佳方式是,基于默认的Buton皮肤,创建一个自定义的Button皮肤。为了让问题简单化,这个例子没有考虑到屏幕DPI的问题。笔者将在系列文章的其他篇幅讨论这些问题。

首先,使用 Adobe Illustrator CS5为Button图形创建一个FXG文件。这个文件将同时展现Button组件的updown皮肤状态。因为这里主要针对触摸输入,所以没有创建over状态。

其次,通过重载MobileSkin的drawBackground()方法,添加对chromeColor样式的支持。或者,也可以手动在up和down的图形里添加固定的背景颜色,然后重载drawBackground() 方法,不作任何事情。disabled 状态仅仅会改变up 状态的 alpha值。移动ButtonSkin类已经默认包含这个功能。

教程第一步:创建FXG图形

根据自己的喜好,可以选择Adobe Flash Professional、Adobe Illustrator或者 Adobe Fireworks构建FXG。你也可以在Flash Builder中手工编辑FXG,然后在MXML文件中移动这个FXG,再用设计视图渲染它,可视化的检查结果。

用 Illustrator 创建 FXG

这个例子用 Illustrator 创建一个药丸形状的Button图形.

  1. 在Illustrator中,选择 File > New。
  2. 为图形命名,例如RoundedButtonExport
  3. 设置 New Document Profile 为Flash Catalyst。
  4. 设置大小为典型的手机尺寸 480 px x 800 px 。
  5. 点击 OK
  6. 用矩形工具新建一个由灰色笔触,渐变填充的矩形。
  7. 在Stroke面板中,找到Align Stroke,选择中间的Align Stroke To Inside选项。默认情况下,笔触都是居中对齐的。笔者将稍候介绍为什么最好避免使用这个默认值。
  8. 修改 X 和 Y 位置为 0.
  9. 选中矩形后,选择 Effect > Stylize > Round Corners。
  10. 指定角的半径为 22 px。
  11. 保存为没有私有数据的 FXG 。

清理 FXG

导出FXG文件后,你可能希望清理一些不需要的标签,例如多余的Group标签或者私有的命名空间数据。这个步骤不是必须的,不过这样做可以让你的FXG更容易让人理解。

RoundedButtonExport.fxg

<?xml version="1.0" encoding="utf-8" ?> <Graphic version="2.0" viewHeight="800" viewWidth="480" ai:appVersion="15.0.2.399" ATE:version="1.0.0" flm:version="1.0.0" d:using="" xmlns="http://ns.adobe.com/fxg/2008" xmlns:ATE="http://ns.adobe.com/ate/2009" xmlns:ai="http://ns.adobe.com/ai/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt" xmlns:flm="http://ns.adobe.com/flame/2008"> <Library/> <Group ai:seqID="1" d:layerType="page" d:pageHeight="800" d:pageWidth="480" d:type="layer" d:userLabel="Artboard 1"> <Group ai:seqID="2" d:type="layer" d:userLabel="Layer 1"> <Group ai:seqID="3" flm:knockout="false" d:type="layer" d:userLabel="RoundedButton"> <Group ai:seqID="4" flm:knockout="false"> <Path x="0.5" y="1" winding="nonZero" ai:seqID="5" data="M21.5 43C9.64502 43 0 33.355 0 21.5 0 9.64502 9.64502 0 21.5 0L197.5 0C209.355 0 219 9.64502 219 21.5 219 33.355 209.355 43 197.5 43L21.5 43Z"> <fill> <LinearGradient x="109.5" y="0" scaleX="43" rotation="90"> <GradientEntry ratio="0" color="#F0F0F0"/> <GradientEntry ratio="0.478788" color="#C8C8C8"/> <GradientEntry ratio="0.50303" color="#BBBBBB"/> <GradientEntry ratio="1" color="#F0F0F0"/> </LinearGradient> </fill> </Path> <Path winding="nonZero" ai:seqID="6" data="M198 1C209.58 1 219 10.4204 219 22 219 33.5796 209.58 43 198 43L22 43C10.4204 43 1 33.5796 1 22 1 10.4204 10.4204 1 22 1L198 1M198 0 22 0C9.8999 0 0 9.8999 0 22 0 34.1001 9.8999 44 22 44L198 44C210.1 44 220 34.1001 220 22 220 9.8999 210.1 0 198 0L198 0Z"> <fill> <SolidColor color="#DDDDDD"/> </fill> </Path> </Group> </Group> </Group> </Group> <Private/> </Graphic>

RoundedButtonCleanup.fxg

<?xml version="1.0" encoding="utf-8" ?> <Graphic version="2.0" xmlns="http://ns.adobe.com/fxg/2008"> <Path x="0.5" y="1" winding="nonZero" data="M21.5 43C9.64502 43 0 33.355 0 21.5 0 9.64502 9.64502 0 21.5 0L197.5 0C209.355 0 219 9.64502 219 21.5 219 33.355 209.355 43 197.5 43L21.5 43Z"> <fill> <LinearGradient x="109.5" y="0" scaleX="43" rotation="90"> <GradientEntry ratio="0" color="#F0F0F0"/> <GradientEntry ratio="0.478788" color="#C8C8C8"/> <GradientEntry ratio="0.50303" color="#BBBBBB"/> <GradientEntry ratio="1" color="#F0F0F0"/> </LinearGradient> </fill> </Path> <Path winding="nonZero" data="M198 1C209.58 1 219 10.4204 219 22 219 33.5796 209.58 43 198 43L22 43C10.4204 43 1 33.5796 1 22 1 10.4204 10.4204 1 22 1L198 1M198 0 22 0C9.8999 0 0 9.8999 0 22 0 34.1001 9.8999 44 22 44L198 44C210.1 44 220 34.1001 220 22 220 9.8999 210.1 0 198 0L198 0Z"> <fill> <SolidColor color="#DDDDDD"/> </fill> </Path> </Graphic>

测试 FXG

导出和清理 FXG之后,你可以把它丢进一个MXML文件中,用设计视图预览,从而快速的验证它是否工作。

尝试以下步骤:

  1. 在Flash Builder中新建一个项目(可以命名为任何名字)。
  2. 在源代码文件夹中加入 FXG 文件。
  3. 切换到设计视图

组件面板将会把FXG文件显示为一个自定义组件。

  1. 拖拽这个组件到设计区域。你可以看到FXG被渲染成原本的大小。
  2. 在设计视图中,尝试改变FXG的宽度。

你将发现圆角在水平方向拉伸(见图1)。但你希望看到的应该是圆角的形状保持不变,而是让图形的中间部分拉伸。为此,需要为FXG加入伸缩网格信息。

图 1.缺少伸缩网格数据

手工添加scale grids

为让grid伸缩适当的伸缩,你需要添加网格的左、右、上和下的位置信息。因为使用了Round Corners风格化选项,我们知道当前的半径是22px。有了这些信息,你可以打开Flash Builder,对FXG文件做如下修改:

  1. <Graphic>根标签中添加scaleGridLeft="22"scaleGridRight="198px" (图形的完整宽度是 220px)

你还需要确保边界的笔锋没有伸缩。

  1. 在同一个标签中,为这个图形添加scaleGridTop="1px"和 scaleGridBottom="43px" (图形的完整高度是 44px)

注意: 对于有些图形,很难找到任意路径上的精确起始点。这种情况下,请使用Illustrator的选择工具去覆盖一个锚点,从而找到它的坐标(见图2)。

图 2.找到锚点坐标

在Graphic根标签添加网格数据之后,查看一下设计视图中的新FXG,你会发现没有什么变化。因为路径信息并没有填满图形的全部尺寸,所以伸缩网格没有变化。

  1. 为了修复这个问题,如下所示,添加一个透明的Rect占据整个图形空间。
  2. 将刚做的修改保存为RoundedButton.fxg.

RoundedButton.fxg

<?xml version="1.0" encoding="utf-8" ?> <Graphic version="2.0" xmlns="http://ns.adobe.com/fxg/2008" scaleGridLeft="22" scaleGridRight="198" scaleGridTop=”1” scaleGridBottom=”43”> <Path x="0.5" y="1" winding="nonZero" data="M21.5 43C9.64502 43 0 33.355 0 21.5 0 9.64502 9.64502 0 21.5 0L197.5 0C209.355 0 219 9.64502 219 21.5 219 33.355 209.355 43 197.5 43L21.5 43Z"> <fill> <LinearGradient x="109.5" y="0" scaleX="43" rotation="90"> <GradientEntry ratio="0" color="#F0F0F0"/> <GradientEntry ratio="0.478788" color="#C8C8C8"/> <GradientEntry ratio="0.50303" color="#BBBBBB"/> <GradientEntry ratio="1" color="#F0F0F0"/> </LinearGradient> </fill> </Path> <Path winding="nonZero" data="M198 1C209.58 1 219 10.4204 219 22 219 33.5796 209.58 43 198 43L22 43C10.4204 43 1 33.5796 1 22 1 10.4204 10.4204 1 22 1L198 1M198 0 22 0C9.8999 0 0 9.8999 0 22 0 34.1001 9.8999 44 22 44L198 44C210.1 44 220 34.1001 220 22 220 9.8999 210.1 0 198 0L198 0Z"> <fill> <SolidColor color="#DDDDDD"/> </fill> </Path> <!-- scale grid fix --> <Rect x="0" y="0" width="220" height="44"> <fill> <SolidColor color="#000000" alpha="0"/> </fill> </Rect> </Graphic>

现在,设计视图中的Button应该伸缩自如了。(见图 3)

图 3.添加了伸缩网格后的组件。

教程步骤2: 创建皮肤类

基于现有的Mobile主题,创建一个新的Button皮肤的过程氛围3个主要步骤。

  1. 新建一个spark.skins.mobile.ButtonSkin的子类。
  2. 在构造函数中,设置FXG类的 upBorderSkin 和downBorderSkin属性。注意,这些是类实行,而不是FXG的实例。同样,为FXG的尺寸信息设置measuredDefaultHeight 和measuredDefaultWidth 属性(参考下面的代码片段)。
  3. 用CSS或者XML设置Button的skinClass 属性。

注意: 这个项目的示例文件中包含RoundedButtonSkinProject.fxp。您可以将这个文件导入Flash Builder,查看完整的应用程序(包括皮肤在内)是如何实现的。

关于chromeColor样式,你有3个选择:

  • 不支持 chromeColor – 重载 drawBackground(),不作任何事情
  • 绘制 chromeColor ,使其符合FXG图形 – 重载 drawBackground() ,用代码绘制 chromeColor。并且为FXG添加alpha值,使得chromeColor 可见。
  • chromeColor填色 - 重载drawBackground(),用工具方法applyColorTransform() 将FXG从基色转变为特定的chromeColor

这个例子演示了第三种方法。

下面的代码是皮肤的最终实现。

RoundedButtonSkin.as

package skins { import skins.assets.RoundedButton; import spark.skins.mobile.ButtonSkin; public class RoundedButtonSkin extends ButtonSkin { private var colorized:Boolean = false; public function RoundedButtonSkin() { super(); // replace FXG asset classes upBorderSkin = skins.assets.RoundedButton; downBorderSkin = skins.assets.RoundedButton; measuredDefaultHeight = 44; measuredDefaultWidth = 220; } override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void { // omit call to super.drawBackground() to apply tint instead and don't draw fill var chromeColor:uint = getStyle("chromeColor"); if (colorized || (chromeColor != 0xDDDDDD)) { // apply tint instead of fill applyColorTransform(border, 0xDDDDDD, chromeColor); // if we restore to original color, unset colorized colorized = (chromeColor != 0xDDDDDD); } } } }

教程步骤3: 测试

下面的代码将定制过的RoundedButtonSkin设置为Button的默认皮肤。为了演示chromeColor和CSS,作者在此为Buttondown状态加入了一些样式。

<fx:Style> @namespace s "library://ns.adobe.com/flex/spark"; s|Button { skinClass: ClassReference("skins.RoundedButtonSkin"); } s|Button:down { chromeColor: #0000FF; color: #FFFFFF; textShadowColor: #000000; } </fx:Style>

在应用程序中添加一些Button后,你应该能看到他们的新皮肤在弹起和按下时的不同状态(见图4)。down状态时的皮肤表明CSS样式已经起作用了。

图 4.拥有新皮肤的Button。

FXG 提示与技巧

在处理FXG时,请记住下列注意事项。

路径 vs 原始图形

路径信息并不总是很容易找到,但有些情况下,路径还可能与一些反锯齿的元素渲染在一起。你每次需要获得的路径信息总是不同的。当你在Illustrator中难以获取路径信息时,不要立刻就期望简化原始图形。

blendMode

图形元素的blendMode 属性默认是设置为 自动的 。当它是auto时候,Flash Player或者AIR运行时将会正确的判断出元素本身是否需要根据alpha值使用blendMode层。

尽可能避免笔触

通常情况下,笔触会横跨图形的边缘(例如,在Illustrator中使用"笔触居中对其")

举例来说,画一条从(0,0)到(0,100)的一条线,它有1个像素宽的笔触。该笔触将会从X轴的-.5延展到.5。但是由于在小数位的像素空间内无法绘制任何东西,Flash将会画一条反锯齿的2个像素宽的线,正好占据该小数宽度位置。为了消除反锯齿的干扰,画图形的时候使用奇数做为笔触大小,使得它能有半个像素的边界。例如,一个占据(10,10)位置的、1个像素宽的SolidColorStroke 笔触,应该实际占位在(10.5,10.5)。

尽可能使用填充矩形,而非有笔触的矩形和线条。如果必须使用有笔触的填充 矩形 ,那可以尝试把它分位两个填充矩形(假设填充万全不透明)。如果要画一个条线,尝试把它转变为一个填充矩形。例如,如果要画一条水平直线,可以创建一个高度等同于线条笔触的填充矩形。

伸缩网格

在用FXG设计图形时,必须考虑到伸缩网格的一些局限。

伸缩网格的值必须在图形的边界之内,并且不能与特性边界重合(也就是说,左边界 scaleGridLeft <scaleGridRight < 右边界)。

如果图形包含Group元素,就不能用伸缩网格。

如果元素使用了alpha 属性,伸缩网格也不能使用。所以,只在笔触和填充元素上应用alpha

FXG规范

FXG的完整规范,请参考《FXG 2.0 - 功能与设计规范》

Flex移动皮肤开发(一)的更多相关文章

  1. Flex移动皮肤开发(三)

    范例文件 mobile-skinning-part3 在关于创建Flex移动皮肤系列文章的第二部分里,我们讨论了屏幕密度(DPI)对组件皮肤以及移动应用布局所带来的影响. 我还展示了如何使用缩放应用, ...

  2. Flex移动皮肤开发(二)

    范例文件 mobile-skinning-part2.zip 在这个讨论创建 Flex 移动 skin 的系列的 第 1 部分 中,我讨论了 Flex 团队在 Mobile 主题中所做的性能优化的原理 ...

  3. Flex自定义组件开发之日周月日期选择日历控件

    原文:Flex自定义组件开发之日周月日期选择日历控件         使用过DateField的我们都知道,DateField 控件是用于显示日期的文本字段,字段右侧带有日历图标.当用户在控件边框内的 ...

  4. Flex修改皮肤样式

    Flex修改皮肤大致有三种方式: (以button为例) 第一种:修改外观 1.flex项目中新建mxml外观.

  5. Flex外包团队—开发工具:Flex4.6新特性介绍

    在今年初,Adobe发布了其第一个支持移动应用程序开发的Flex SDK和Flash Builder版本.Flex 4.5引入了一组移动优化的组件和移动优化的应用程序框架,而Flash Builder ...

  6. 使用Flex 和 Red5开发简单视频直播功能

    Flex 是一个高效.免费的开源框架,可用于构建具有表现力的 Web应用程序,这些应用程序利用Adobe Flash Player和Adobe AIR, 可以实现跨浏览器.桌面和操作系统.虽然只能使用 ...

  7. Flex scroller皮肤的使用

    Flex4 scroller 自定义皮肤 十月 15, 2010 Posted by admin flex4里引入了sparkSkin, spark包里的可视控件可以通过指定skinClass的值来修 ...

  8. Flex自定义组件开发 - jackyWHJ

    一般情况下需要组件重写都是由于以下2个原因: 1.在FLEX已有组件无法满足业务需求,或是需要更改其可视化外观等特性时,直接进行继承扩展. 2.为了模块化设计或进一步重用,需要对FLEX组件进行组合. ...

  9. Flex自定义组件开发

    一般情况下需要组件重写都是由于以下2个原因:1.在FLEX已有组件无法满足业务需求,或是需要更改其可视化外观等特性时,直接进行继承扩展.2.为了模块化设计或进一步重用,需要对FLEX组件进行组合.而F ...

随机推荐

  1. oracle中nvl函数

    最近在修改项目中一个统计的bug,统计出的钱数不对,因为不是自己开发的模块,经过分析流程找到了统计的sql. sum(f_msmoney)+sum(f_fkmoney) as total, 上面这段是 ...

  2. <meta http-equiv="refresh" content="0; url=">

    原文:http://www.cnblogs.com/net2/archive/2010/11/29/1890874.html 页面定期刷新,如果加url的,则会重新定向到指定的网页,content后面 ...

  3. JMS连接WMQ及收发消息

    因为对JMS的了解也只算入门级,有些概念也很模糊,不过,卤煮会尽可能去介绍的.另外,sample code都调试过可以跑. 1.神马是JMS? jms即Java消息服务(Java Message Se ...

  4. 一个很好玩的命令:stty

    stty命令修改终端命令行的相关设置.语法stty(选项)(参数)选项-a:以容易阅读的方式打印当前的所有配置:-g:以stty可读方式打印当前的所有配置.参数终端设置:指定终端命令行的设置选项.实例 ...

  5. 隐藏Nginx版本号的安全性与方法

    搭建好nginx或者apache,为了安全起见我们都会隐藏他们的版本号,这边讲的是nginx的版本号,如果你也想隐藏apache的版本号,那请点前面的链接.请看nginx版本号信息隐藏文章. Ngin ...

  6. SQL 复习二(数据查询语言)

    1.1 数据查询语言 DQL就是数据查询语言,数据库执行DQL语句不会对数据进行改变,而是让数据库发送结果集给客户端. 语法: SELECT selection_list /*要查询的列名称*/ FR ...

  7. K-means算法简介

    K-means 算法是无监督的 聚类算法,算法简单,有效. K-means算法: 输入参数: 指定聚类数目 k,训练集 X 输出 : k 个聚类 算法描述: K-means 算法 是一个 迭代算法,每 ...

  8. Python字符串的encode与decode研究心得——解决乱码问题

    转~Python字符串的encode与decode研究心得——解决乱码问题 为什么Python使用过程中会出现各式各样的乱码问题,明明是中文字符却显示成“/xe4/xb8/xad/xe6/x96/x8 ...

  9. sql数据库中查询第几条到第几条的数据

    通用方法: select top 500 * from (select top 1000 * from UserSearchDatas order by ID) a order by ID desc ...

  10. ucos信号量集源码分析

    在实际的应用之中,一个任务经常需要等待多个信号量的同时生效,或者说任务需要根据多个信号量的组合作用的结果来决定任务的运行方式,为了实现这种多信号量组合的功能,ucos实现了信号量集的特殊结构. 信号量 ...