【转】深入浅出Flex组件生命周期Part2 ─ 使用ActionScript3开发
原文地址:http://www.donglongfei.com/2011/07/actionscript-3-customize-mobile-spark-component-part2/Part2将使用AS3为Part1中已经创建的component类定制Skin类五. 创建CSS我们首先创建将要在Skin类中使用的CSS。样式文件为Style.css,建在src的styles目录下。 代码如下:
/* CSS file */ @namespace s "library://ns.adobe.com/flex/spark"; @namespace components "com.mark.demos.components.*"; components|MobileItemRenderer { fontSize: 20; color: #FF0000; font-family: "_sans"; min-height: 50; padding-left: 10; } 为了应用样式,在主应用文件(本例为SparkItemRendererDemo.mxml)中加入样式声明: <fx:style source="styles/Style.css"/> 六. AS3创建Skin类:MobileItemSkin.as下面,我们的重头戏开始了:使用AS3为之前的component类MobileItemRenderer创建配套的Skin类。 1. 创建MobileItemSkin.as在com.mark.demos.skins包下创建ActionScript类MobileItemSkin.as,该类继承spark.skins.mobile.supportClasses.MobileSkin。 使用Flex开发移动应用项目时,Flex4.5提供了一套专为移动设备优化了的类库和组件。这些类和组件为移动设备的优化包括性能和DPI自适应等等,其中MobileSkin发挥了非常重要的作用,这个类是所有适配移动应用的ActionScript Skin类的基类。 我们在Part4会解释如何使用AS3开发适配Web或桌面的Spark组件的Skin类,这时我们会继承spark.skins.SparkSkin。 值得注意的是MobileSkin的父类是UIComponent。(在Part3,我们会深入讨论MobileSkin和SparkSkin。)现在,让我们跳出代码,重新考虑Spark的Skinnable组件这件事情。现在我们已经知道,要创建两个类,一个component类,一个skin类,而两个类都是继承于UIComponent,从这一点上来说两个类没什么本质上的区别,只不过通过Flex框架代码,来让一个类管理数据和逻辑,一个类管理样式和外观。 下面我们来逐步完成Skin类:MobileItemSkin.as,在这个过程中,您不碍多想一想这个类是如何和component类协同的呢? 2. hostComponent每个Skin类都要声明它的宿主:hostComponent。Skin类的存在目的就是为Component类服务的,失去了Component,Skin类就失去了意义。一列失去灵魂的列车奔驰得再快,也没有任何价值。
hostComponent帮助Skin从Component中“拉”数据,通过这种方式获取开发者设定在component类上的属性以及相关变化。 private var _hostComponent:MobileItemRenderer; public function get hostComponent():MobileItemRenderer { return _hostComponent; } public function set hostComponent(value:MobileItemRenderer):void { _hostComponent = value; } 3. createChildren()接下来,我们要为component类来“组装”Skin。在本例中,就像MobileItemRenderer中通过SkinPart元数据标签已经说明的,我们要为其装配一个StyleableTextField或其子类类型的文本。我们并不是一步到位的为Skin生成该对象,并安装到位。还记得吗?Flex架构非常重要的工作就是“延迟计算”,因此,首先,我们要做的只是“创建子对象”: createChildren。 public class MobileItemSkin extends MobileSkin { public var labelDisplay:StyleableTextField; 下面是createChildren方法真正完成创建labelDisplay的方法,在该方法中使用addChild把labelDisplay添加在显示列表中。 override protected function createChildren():void{ addLabel(); } private function addLabel():void{ labelDisplay = new StyleableTextField(); addChild(labelDisplay); } 4. commitProperties()如果你了解UIComponent的生命周期,你应该知道在UIComponent的生命周期中,Flex会依序调用createChildren,commitProperties,measure,layoutChrom和updateDisplayList方法,这些方法依次完成了各自专有的任务,其中方法commitProperties,measure和updateDisplayList会在UIComponet创建后,调用invalidateProperties,invalidateSize和invalidateDisplayList方法后,在下一帧时再次被调用, override protected function commitProperties():void{ super.commitProperties(); styleLabel(); } private function styleLabel():void{ if(labelDisplay && hostComponent && hostComponent.data){ if(currentState=="selected"){ labelDisplay.text= "远离高铁"; } if(currentState=="normal"){ labelDisplay.text= hostComponent.data.label; } labelDisplay.defaultTextFormat = new TextFormat( getStyle( "fontFamily" ),getStyle( "fontSize" ),getStyle("color") ); labelDisplay.commitStyles(); } } 需要说明的是styleLabel方法。
5. measure()我们已经设定了labelDisplay的外观,接下来,我们需要告知Flex本组件的默认最小尺寸和默认尺寸:覆盖measure方法。 override protected function measure():void{ super.measure(); var h:Number=0; var w:Number=0; if(labelDisplay){ w=labelDisplay.textWidth + getStyle("paddingLeft"); h=labelDisplay.textHeight; } measuredMinHeight = getStyle( "minHeight" ); measuredHeight = Math.max(h,measuredMinHeight); measuredWidth=w; measuredMinWidth=Math.max(unscaledWidth,w); } 在上述代码中,我们通过getStyle获取CSS样式单中指定的minHeight来确定最小值。如果你深入阅读LabelItemRenderer.as和IconRenderer.as源码,你会发现measure方法要考虑的问题很多,本例中做了简化。 6. layoutContents()方法按照UIComponet的“填空题”应试法则,下面我们应该完成的是UpdateDisplayList方法,来实现最终的布局。但是MobileSkin覆盖了UIComponent类的UpdateDisplayList方法,新的UpdateDisplayList方法摘录于下: override protected function updateDisplayList(unscaledWidth:Number,unscaledHeight:Number):void { graphics.clear(); super.updateDisplayList(unscaledWidth,unscaledHeight); layoutContents(unscaledWidth,unscaledHeight); if (useSymbolColor) applySymbolColor(); drawBackground(unscaledWidth,unscaledHeight); } 可以看到,updateDisplayList的主要工作被分为layoutContents和drawBackground两个方法。因此,对于创建移动应用组件皮肤,我们很少会直接覆盖updateDisplayList方法,而是覆盖layoutContents和drawBackground方法。 override protected function layoutContents(unscaledWidth:Number,unscaledHeight:Number):void{ var labelWidth:Number=0; var labelHeight:Number=0; if(labelDisplay){ labelDisplay.width=unscaledWidth; labelDisplay.x = getStyle( "paddingLeft" ); labelDisplay.y = (unscaledHeight - labelDisplay.textHeight ) / 2; } // we draw a separator line between each item var lineY:int = unscaledHeight -1; graphics.clear(); graphics.lineStyle( 1 ); graphics.moveTo( 0,lineY ); graphics.lineTo( unscaledWidth,lineY ); } 我们设置了labelDisplay的坐标,并且在其下边缘绘制了一条1像素的横线。 7. 更新状态实际上,现在你就可以使用MobileItemRenderer了,但是如果你把他作为itemRenderer赋给List后,运行后,你会发现 MobileItemRenderer并没有及时的响应用户操作,更新状态。 override protected function commitCurrentState():void{ super.commitCurrentState(); styleLabel(); } 如果你看过Part1,你会记得,我们已经为Component类MobileItemRenderer.as声明了SkinState:normal和selected。在用户选中本item后,List会调用selected的set方法,然后该方法又会调用invalidateItemState来通知Flex更新状态。我们也按照要求,让Component类MobileItemRenderer乖乖的提供了方法getCurrentSkinState,来通知某人我目前的状态。而在我们的Skin类中,很明显,某个神秘先生又通过currentState变量告知了当前状态,而前提是,我们要乖乖的实现commitCurrentState方法。 override protected function commitCurrentState():void{ super.commitCurrentState(); styleLabel(); } 七. 调用MobileItemRenderer,完成View目前,我们已经完成了Component类和Skin类,比较简陋,但是在Part3您会看到他的价值。 < ?xml version="1.0" encoding="utf-8"?> <s:view xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" title="主页视图" xmlns:components="com.mark.demos.components.*"> <fx:declarations> <!-- 将非可视元素(例如服务、值对象)放在此处 --> </fx:declarations> <s:list width="100%" height="100%" labelField="label"> <s:dataprovider> <s:arraycollection> <fx:object label="珍惜生命"/> <fx:object label="珍惜生命"/> <fx:object label="珍惜生命"/> <fx:object label="珍惜生命"/> <fx:object label="珍惜生命"/> <fx:object label="珍惜生命"/> </s:arraycollection> </s:dataprovider> <s:itemrenderer> <fx:component> <components:mobileitemrenderer /> </fx:component> </s:itemrenderer> </s:list> </s:view> 运行后,您会得到如下结果: 八. 未完待续…上帝花七天创造了世界,问题是,他是怎么做的呢? 如果你依照步骤看完了Part1和Part2,那么,这也许是最“疑惑重重”的教程之一,我们为您演示了如何创建一个Spark架构下Skinnable组件类。但是,又带了了如此多的问题:
朋友,让我们在Part3中再见,揭开这些为什么。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |