[转]深入浅出Flex组件生命周期Part1─ 使用ActionScript3开发Spa
前言Flex开发移动应用时,出于性能考虑,需要使用AS3编写组件Skin,而不是使用MXML。实际上,通过使用AS3编写组件皮肤,开发者可以更深入的了解Flex的组件生命周期,无论是对于移动应用开发还是传统的桌面或者Web应用开发,都大有裨益。 关于本系列文章Spark架构的目的就是分离组件行为和可视化。在Spark框架中,组件的数据和逻辑行为由组件类负责,而可视化剥离到Skin中。组件和其Skin间维护一个所谓的“契约”。本文不具体解释Spark架构的基本概念,如果您不了解Spark的基础知识,可以参考Deepa姐姐的文章 “?Spark架构和组件集的简要概述”。 本文将为你揭示Spark架构背后的秘密。本文介绍的内容和例子是基于Flex4.5的移动架构上,但是其中关于Spark架构的介绍99%通用于非移动框架的Spark组件。为了更好的理解本文,建议您下载Flex4.5 SDK源码。 此外,亲,我们在本文中不会针对Spark架构容器展开讨论,好吗? ? ?…… 好滴。 我们的终极目标Flex移动应用中,最频繁使用的组件之一可能就是spark.components.LabelItemRenderer和spark.components.IconItemRenderer了。IconItemRenderer继承自LabelItemRenderer,而LabelItemRenderer扩展了UIComponent。
Spark体系的Skinnable组件应该是继承自SkinnableComponent。每一个组件包含两个文件,一个是组件类(继承自SkinnableComponent),一个是皮肤类。很显然,IconItemRenderer和LabelItemRenderer都不属于SkinnableComponent,其弊端在于每次如果你需要深度定制ItemRenderer,都不得不扩展这两个类,覆盖其中相关方法。 Part1的目标是使用ActionScript创建一个真正Skinnable的ItemRenderer。为了简单起见,我们以LabelItermRenderer为例。 值此铁道部为难之际,草民望借此本系列文章,帮助屁民程序员们,更加深入的理解Spark架构,为创建一个和谐的社会而努力。 一. 开发Spark组件和mobile应用适用的Skin类的步骤B车追上A车,需要一个步骤:电A车! 而创建Spark架构下Skinnable的组件需要两个主要步骤:
最复杂的是把大象关进冰箱,具体方法超出本文讨论范畴。 二. 创建Flex手机项目启动FlashBuilder4.5,点击“文件->新建”创建“Flex手机项目”,如下图。 跟随向导选择手机操作系统和应用模板。本例中选择的模板是”基于视图的应用程序”。FlashBuilder将为我们创建空的手机应用项目,在本例中,我们最终所创建项目的项目结构如下: 三. 创建Component类1. 创建继承SkinnableComponent,实现IDataRenderer和IItemRenderer的AS3类:MobileItemRenderer.as在src目录下,创建路径为com.mark.demos.components的package。在该package下,通过菜单“新建->ActionScript Skinnable组件”创建Skinnable组件类。 在创建向导中(如下图),指定该类名为MobileItemRenderer。可以看到该类的超类默认设置为spark.components.supportClasses.SkinnableComponent。SkinnableComponent是所有符合Spark架构的Skinnable组件类的基类,其父类是UIComponent。我们在Part3中会深入讨论这个类。 同时,由于我们要创建可以被List使用的itemRenderer,因此需要实现IDataRenderer和IItermRenderer两个接口。把两个接口添加到接口列表中。 2. 完成需要的IDataRenderer和IItemRenderer接口方法处于简化目的,本例中并不完成所有的接口方法。我们主要实现data、itemIndex和selected的set和get方法。实现IDataRenderer接口的data的set和get方法之目的在于使我们自定义的MobileIterRenderer能够从List中获取到数据。实现IItemRenderer接口的itemIndex和selected的set和get方法的目的是,能够从List获取到条目的用户选择状态和index。关于两个接口的具体说明请参见AS3参考中的IItemRenderer和IDataRenderer说明。 首先创建三个变量:
private var _itemIndex:int; private var _data:Object; private var _selected:Boolean=false; 接下来,分别完成data的set和get方法,以及itemIndex的get方法。 itemIndex的set方法比较复杂,本例暂不实现。如果你希望进一步了解,可以参考spark.components.LabelItemRenderer类源码中的itemIndex set方法。 [Bindable("dataChange")] public function get data():Object { return _data; } /** * @private */ public function set data(value:Object):void { _data = value; if (hasEventListener(FlexEvent.DATA_CHANGE)) dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE)); } public function get itemIndex():int { return _itemIndex; } public function set itemIndex(value:int):void { //此方法本例中暂不实现。 } public function get selected():Boolean { return _selected; } public function set selected(value:Boolean):void { if( _selected != value) { _selected = value; } } ?? ?我们之后还会修改selected的set方法,因为该属性的变化会触发我们自定义的component类MobileItemRenderer的状态变化。 3. 声明SkinPartSkinPart实际上最终就是Skin类的一个子组件,由Skin类将其加入到显示列表,或者从其中删除。而component类则声明本component需要哪些SkinPart,对应的Skin类要严格照办。 public class MobileItemRenderer extends SkinnableComponent implements IDataRenderer,IItemRenderer { [SkinPart(required="true")] public var labelDisplay:StyleableTextField; ...... 4. 覆盖PartAdded和partRemoved方法 当Skin类将对应的SkinPart加入到显示列表中时(通过addChild),component类会调用partAdded方法。当移除时,会调用partRemoved方法。 override protected function partAdded(partName:String,instance:Object) : void { super.partAdded(partName,instance); if(partName=="labelDisplay"){ labelDisplay.addEventListener(MouseEvent.DOUBLE_CLICK,changeLabel); } } override protected function partRemoved(partName:String,instance:Object) : void { super.partRemoved(partName,instance); if(instance==labelDisplay){ labelDisplay.removeEventListener(MouseEvent.DOUBLE_CLICK,changeLabel); } } private function changeLabel(event:MouseEvent):void{ var obj:StyleableTextField=event.target as StyleableTextField; obj.text="小心雷电"; } partAdded方法的参数partName说明了加入到显示列表中的部件名称,该名称对应之前SkinPart声明的变量名(如果有多个SkinPart,则应对应其中某个)。instance为加入到显示列表中的SkinPart对象,在本例中即为Skin类通过addChild加入进来的StyleableTextField类型的labelDisplay对象。 我们可以如partAdded方法中一样通过partName来判断,也可以如partRemoved方法中使用instance来判断加入的对象是哪个SkinPart。 5. 声明SkinState接下来,为我们的组件声明状态。MobielItemRenderer只有两个状态normal和selected。状态声明要在类外。
[SkinState("normal")] [SkinState("selected")] public class MobileItemRenderer extends SkinnableComponent implements IDataRenderer,IItemRenderer { 默认情况下,组件的状态为normal,当List通知item被选中后(通过selected的set方法设置selected为true时),则组件状态变为selected。当selected变为false时,则状态切换回normal。在Flex应用中,组件数据模型的变化会引发组件状态的变化,而状态的变化则会触发可视界面的变化。因此,component类本身并不负责处理状态,component类仅仅声明了Skin类的状态,并根据数据模型的变化来反馈当前状态,而Skin类要负责针对不同状态实现可视化效果。 (part3问题四:invalidateSkinState是如何通知Skin类的呢?) 6. 覆盖getCurrentSkinState方法component类通过getCurrentSkinState方法来通知Skin类当前状态。在该方法中,我们通过component类的数据模型来判断当前处于哪种状态。 override protected function getCurrentSkinState():String{ var returnState:String="normal1"; if(_selected){ returnState="selected"; } return returnState; } (part3问题五:Skin类是如何调用getCurrentSkinState的呢?) 目前为止,我们已经创建了一个非常基本的component类,实际上我们没有为该类实现一点点关于可视化的部分,这写任务都将由Skin类来完成。而Spark组件生命周期中最神秘也最有趣的地方就是,component类是如何同与其对应的skin类相互配合互动的。 原文地址:http://www.donglongfei.com/2011/07/actionscript-3-customize-spark-component-part1/?replytocom=176 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |