首页 > 设计 > WEB开发 > 正文

7.2.使用ClassFactory 生成渲染器

2023-08-02 23:09:34
字体:
来源:转载
供稿:网友
7.2.1.问题
你想要在运行时改变List 或DataGridColumn 的渲染器,或改变渲染器的属性。
7.2.2.解决办法
使用一个ClassFactory 对象做为ItmeRdnerer。ClassFacotry 实现了IFactory 接口,你只要传一个类对象(即Class 类型的对象)给它,它就可以生成这个类的对象。
7.2.3.讨论
这种解决办法,使用了工厂设计模式,并且使用了flex 的一些内部机制。在使用工厂模式时,你需要指定一个类型给工厂,这个类型的对象会被工厂类产生并做为渲染器使用,并且工厂类可以控制渲染器的生命周期。那么我们如何把类型指定给工厂类呢?我们需要传一个类对象给工厂类,正如下边的代码:
+展开
-ActionScript
var factory:ClassFactory = new ClassFactory(oreilly.cookbook.SevenTwoFactory);

在这里原文提到了SevenTwoFactory 继承IFactory 等等,大家需要多了解一些ClassFactory 的相关知识,在实际操作中,我们一般都是创建ClassFactory 的实例赋值itemRenderer,因为set itemRenderer 方法需要的参数是IFactory 类型,系统最终会用这个factory 对象实例我们的renderer。也就是说,我们给系统的不是一个renderer 而是一个能产生renderer 的factory,在系统需要时自己用factory 产生renderer 的实例,也许细心的读者会发现,当我们写mxml 的时候,itemRenderer 的属性可以赋值一个类路径,这就是因为flex 对mxml 里的itemRenderer 做了一些特殊的功能,如果你传的是一个类路径,系统帮你创建一个ClassFactory。,在本节的例子中其实在我们的应用中SevenTwoFactory 只需是一个普通的类就OK 了,我看了本节后边evenTwoFactory 的代码,

觉得作者这样做没有必要,所以还是不扯SevenTwoFactory 和IFactory 的关系了,不然反倒让大家糊涂。在这里我们只需要知道, ClassFactory 的实例,赋值给list 或DataGrid的itemRenderer 属性,系统会帮我们产生item renderer。
+展开
-XML
<cookbook:PurgeList id="listitemRenderer="{factory}width="300"/>

在下边的范例中,提供了一个方法用于访问ListBase 中的一个protected 方法purgeItemRenderers,其实原文中并没有提到purgeItemRenderers 的用法和出处, 所以我个人认为,作者写在这里让人觉得很突兀,其实purgeItemRenderers 是ListBase 的一个protected 的方法。这个方法主要是用于清除所有ItemRenderer 和一些相当的信息,其实这个方法是应用于ListBase 自己的内部机制,它会在ItemRenderer 被替换时调用。因为ListBase 是Tree、List、DataGrid 等这些类的父类,所以这些类也继承了此方法。其实大家可以不用看下边这段代码,我在此节最后提供了一段可以运行的简单例子,比原文的例子更具代表性,更简单。还有,即然原文这里提到了tree 我们就顺便提一下tree 它的itemrenderer 不象list dataGrid 那样。Tree 的item renderer 必须extends TreeItemRenderer,这些,我们将在本章的12 节细讲。
+展开
-ActionScript
public function clearList():void {
this.purgeItemRenderers();
this.invalidateDisplayList();
}

我们看一下,ClassFactory 是如何工作的。
+展开
-XML
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="setType()"
xmlns:cookbook="oreilly.cookbook.*">

<mx:Script>
<![CDATA[
[Bindable]
private var
factory:ClassFactory;
private function setType():void {
factory = new ClassFactory(SevenTwoFactory);
factory.properties={type:SevenTwoFactory.HORIZONTAL};
}

]]>
</mx:Script>
<cookbook:PurgeList id="list"
itemRenderer="{factory}width="300"/>

<!—- toggle between the horizontal and vertical item rendering
-- >

<mx:Button id="toggleStyletoggle="true"
click="toggleStyle.selected ?
factory.properties = {type:SevenTwoFactory.VERTICAL} :
factory.properties={type:SevenTwoFactory.HORIZONTAL}
"
label="style"/>

<!—- here we redraw the whole list to use our new renderers --
>

<mx:Button id="toggleDPtoggle="trueclick="list.clearList(),
list.dataProvider = DataHolder.genericCollectionOne,
this
.invalidateDisplayList()
label="generate"/>

</mx:HBox>

ClassFactory 会把properties 对象里的数据传给它生成的对象。IFactory 这个接口只有一个方法,如下是这个方法的一个实现:
+展开
-ActionScript
public function newInstance():* {
return new MyClass();
}

每当需要一个新对象时,ClassFactory 就会调用这个方法。ClassFactory 会负责把一个初始化的数据传入新建的对象。上边的例子就是这样做的:
+展开
-ActionScript
factory.properties = {type:SevenTwoFactory.HORIZONTAL};

在这个新建对象的类中有一个setter 方法捕获这个操作:
+展开
-ActionScript
public function set type(value:String):void {
_type = value;
invalidateDisplayList();
}

ClassFactory 会试图把properties 里的所有属性都赋给它新产生的对象里。这就是为什么新的数据可以在运行时置入item renderer。你可以利用这一点特性,传一些你感兴趣的数据给item renderer。在这里原文见意我们写renderer 的时候尽量不要继承mx.containers 下的那些容器,如HBox VBox Canvas 等,作者见意我们继承UIComponent,这样会使renderer 效率高很多。同时还提到下边例子中用flash.text.TextField 替代mx.controls.Text 和Label 也是同样的目地。作者同时还建议我们在写程序时尽量多使用一些低级别、轻量级的组件从而提高我们程序的性能。之前我在网上也看到一篇关于提高itemRenderer 性能的文章,也提到了这些。
+展开
-ActionScript
package oreilly.cookbook {
import flash.text.TextField;
import mx.controls.Label;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.IFactory;
import mx.core.UIComponent;
import mx.events.FlexEvent;
//the class implements IListItemRenderer to ensure that it will function
//properly with the List, and IFactory, to ensure that it can be used in
//a factory
public class SevenTwoFactory extends UIComponent implements IFactory, IListItemRenderer{
//here are our two layout types that we'llusetothe'type'
public static const HORIZONTAL:String = "horizontal" ;
public static const VERTICAL:String = "vertical" ;
//by default we'll go with horizontal
private var _type:String = HORIZONTAL;
private var _data:Object;
//here are our three TextFields
private var nameLabel:TextField;
private var ageLabel:TextField;
private var appearanceLabel:TextField;
//this is the property we'll set in the properties of the
//ClassFactory
public function set type(value:String):void {
_type = value;
invalidateDisplayList();
}
public function get data():Object { return _data; }
// we need to do this to determine the correct size of the renderer
override protected function measure():void {
super.measure();
if (this._type == HORIZONTAL) {
measuredHeight = nameLabel.height;
measuredWidth = nameLabel.width + ageLabel.width +
appearanceLabel.width + 10;
else {
measuredWidth = appearanceLabel.width;
measuredHeight = nameLabel.height + ageLabel.height+ appearanceLabel.height + 10;
}
height = measuredHeight;
width = measuredWidth;
trace("w"+this.measuredWidth+ " "+this.measuredHeight+" "+this.width+" "+this.height);
}
//set all the TextFields with the correct data by parsing out
// the data value. We create the TextField only if we need it.
public function set data(value:Object):void {
_data = value;
if (_data.name != null && nameLabel == null ) {
nameLabel = instantiateNewLabel(_data.name);
else {
nameLabel.text = _data.name;
}
if (_data.age != null && ageLabel == null ) {
ageLabel = instantiateNewLabel(_data.age);
else {
ageLabel.text = _data.age;
}
if (_data.appearance != null && appearanceLabel == null )
{
appearanceLabel = instantiateNewLabel(_data.appearance);
else {
appearanceLabel.text = _data.appearance;
}
setStyle("backgroundColor", 0xddddff);
invalidateProperties();
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
//Since the UIComponent doesn't possess any layout logic, we need to
//do all of that ourselves
override protected function updateDisplayList(unscaledWidth:Number,unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
var sum:Number = 0;
if (this _type == HORIZONTAL) {
nameLabel.x = 0;
sum += nameLabel.width;
ageLabel.x = sum;
sum += ageLabel.width;
appearanceLabel.x = sum;
else {
nameLabel.y = 0;
sum += nameLabel.height;
ageLabel.y = sum;
sum += ageLabel.height;
appearanceLabel.y = sum;
}
}
private function instantiateNewLabel(value:*):TextField {
var text:TextField = new TextField();
addChild(text);
text.text = String(value);
text.width = text.textWidth + 5;
text.height = text.textHeight + 5;
return text;
}
// here, finally is the Factory method, that will return
// a new instance for each renderer needed
public function newInstance():* { return new SevenTwoFactory(); }
}
}

下边是我为大家准备的一个简单的小例子:
DynamicRenderer.mxml:
+展开
-XML
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute">

<mx:Script>
<![CDATA[
public function changeRenderer():void {
if (btnChange.label == "change to big"){
list.itemRenderer = new
ClassFactory(BigIconRenderer);
//这里正如我在上边所说,这里只要传一个普通类就可以了,但实际开发中我们这里一般都传一个自定义组件,或类似的东西。
btnChange.label = "change to small" ;
}else {
var cf:ClassFactory = new ClassFactory(SmallIconRenderer);
//在下边的代码中我实例大家如何利用ClassFactory的properties属性给新生产的对象赋初值。
//cf.properties = {xx:"heihei};
var o:Object = new Object();
o.xx = "xixi";
cf.properties = o;
//上边的写法,和上边注掉的写法都是可以的.但我觉得更负责的写法是:
//if(cf.properties == null){
// cf.properties = new Object();
//}
//o.xx = "hehe";
list.itemRenderer = cf;
btnChange.label = "change to big" ;
var c:Class = SmallIconRenderer;
var x:Object = new c();
}
}

]]>
</mx:Script>
<mx:List id="listwidth="300height="300"
itemRenderer="SmallIconRenderervariableRowHeight="true">

<mx:dataProvider>
<mx:Object label="this is item A"/>
<mx:Object label="this is item B"/>
<mx:Object label="this is item C"/>
<mx:Object label="this is item D"/>
</mx:dataProvider>
</mx:List>
<mx:Button id="btnChangelabel="change to big"
click="changeRenderer()x="308y="10"/>

</mx:Application>


SmallIconRenderer.mxml
+展开
-XML
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]
public var item:Object = new Object();
[Bindable]
public var xx:String;

]]>
</mx:Script>
<mx:Label text="{data.label}"/>
<mx:Text text=""/>
<mx:Button label="{xx}"/>
</mx:HBox>


BigIconRenderer.mxml
+展开
-XML
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]
public var item:Object = new Object();

]]>
</mx:Script>
<mx:Label text="{data.label}fontSize="20fontWeight="boldcolor="0xff0000"/>
<mx:Text text=""/>
</mx:HBox>
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表