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

7.10.为项渲染器和项编辑器应用运行时样式

2023-08-02 23:09:29
字体:
来源:转载
供稿:网友
7.10.1.问题
你需要在运行时修改itemRenderer 或itemEditor 的一些属性。
7.10.2.解决办法
ListBase 和DataGrid(原文是DataGridColumn,可能是作者搞错了。)都有makeRowsAndColumns 方法,继承并重写它。
7.10.3.讨论
我们可以在makeRowsAndColumns 方法中,通过一个循环完成对itemRenderer 样式的设置。
+展开
-XML
<mx:List xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
private var _rendererStyle:String;
//here's the most effective way to ensure that we draw
//the item renderers only when we need them
override protected function makeRowsAndColumns(left:Number, top:Number,right:Number, bottom:Number, firstCol:int,firstRow:int, byCount:Boolean=false,rowsNeeded:uint=0.0):Point{
var pt:Point;
pt = super.makeRowsAndColumns(left, top, right,bottom, firstCol,firstRow, byCount, rowsNeeded);
if(_rendererStyle != null) {
rendererStyle = _rendererStyle;
}
return pt;
}
public function set rendererStyle(styleName:String):void {
_rendererStyle = styleName;
if( collection != null) {
var i:int = 0;
do {
try {
var comp:UIComponent =(indexToItemRenderer(i) as UIComponent);
if(comp.styleName == _rendererStyle){
comp.styleName = _rendererStyle;
comp.invalidateDisplayList();
else { continue ; }
catch (err:Error){}
i++;

while( i < rowCount )
}
}
public function get rendererStyle():String { return _rendererStyle; }

]]>
</mx:Script>
</mx:List>

首先指出上边的代码中有错误,set rendererStyle 方法中if(comp.styleName ==_rendererStyle){ 中的“==”应该改为“!=”。

原文在这里强调了,makeRowsAndColumns 方法和indexToItemRenderer 方法。使用indexToItemRenderer 得到itemRenderer,得到itemRenderer 设置样式就不难了。那么makeRowsAndColumns 是做什么用的呢?当List 或DataGrid 有滚动条的时候,我们拖动滚动条时,如果有新的item 显示时,会重建或创建新的itemRenderer,这些新显出来的itemRenderer 是没有样式的,所以我们要在makeRowsAndColumns 里给这些itemRenderer设置样式。

原文讲的东西很少,我们再深入的谈一下上边的例子:
在上边的例子中,使用了rowCount,这种方式只适用于List。indexToItemRenderer方法是从现有己显示的itemRenderer 里取一个。rowCount 是当前list 的高度可以显示几行。对于list 来说,能显示多少行,就代表有多少个itemRenderer。但这个rowCount 用到这里又是不对的,原因我们在后边讲。如果大家看过API,API 中对rowCount 的说明,会发现API 重点强调了DataGrid,对于DataGrid 要注意,因为DataGrid 的表头也算一行,也就是说如果DataGrid 里只显示了三行数据,rowCount 为四。但我在实际的测试中,DataGrid 的rowCount 并没有把header 算上,如果大家感兴趣可以自己考证一下。

下面说一下,前面提到的:“rowCount 用到这里不对的”。可能是作者误解了indexToItemRenderer 方法的用法,如果大家看过indexToItemRenderer 这个方法的源码,大家就会发现上边这个例子是有bug 的,下面我把源码粘出来:
+展开
-ActionScript
public function indexToItemRenderer(index:int):IListItemRenderer {
var firstItemIndex:int = verticalScrollPosition -offscreenExtraRowsTop;
if(index < firstItemIndex ||index >= firstItemIndex + listItems.length){
return null ;
}
return listItems[index - firstItemIndex][0];
}

首先大家要理解listItems 里保存的是当前可见的itemRenderer,并不是所有的itemRenderer。而index 是dataProvder 中某一个元素的序列。其实关于index 在API 中己经描述的很清楚了,这就是为什么上边的代码中要index – firstItemIndex(index 在可见itemRenderer 中的序列)。我们再回过头看原文中的代码,那个do{} while()循环,它使用i < rowCount 控制循环的结束,我们在前边讲过,rowCount 是可见行数,可见行数必然小于等于dataProvider 的行数。所以在这个循环里可能不会把所有可见itemRenderer都循环到。如果大家还不明白,我可以举几个数子给大家:假如dataProvider 的行数为10,List 的高度只能显示3 行。这时我们滚动条拖到第5 条记录处,即显示5、6、7 三条记录。这时运行原文中的do{}while(),i 会是0、1、2,分别把这三个数据传入indexToItemRenderer,在些方法中firstItemIndex 为5 即滚动条所在位置,index 始终小于firstItemIndex,所以indexToItemRenderer 始终返回null,在这种情况下,do{}while()没有循环到任何一个itemRenderer。

那么上边的这个bug 如何解决呢?有两种办法,我们先说第一种:把while 的条件设置的足够大,比如我们可以使用dataProvider 的长度。另一种办法我们在后面讲。

原文还有另一个隐含问题,文章的开头一直把List 和DataGrid 放在一起谈,可是到了例子中却绝口不提DataGrid。那么我在这里补充一下DataGrid:如果大家测试过,会发现:如果你的DataGrid 里只有一列itemRenderer,使用上边的方式是没问题的(当然要改掉rowCount)。(还要插一句,必须在DataGrid 里显示的写一个itemRenderer,才可以看到效果,但在List 中不必)。

可是如果有两列或三列itemRenderer 呢?经过测试发现,以上的方式只能处理第一列itemRenderer。问题出在哪里了呢?我们看一下indexToItemRenderer 的原码,其实listItems 是一个二维数组,但在indexToItemRenderer 里当做一维使用了。所以我们不能再使用indexToItemRenderer 了(通过前边所讲,相信大家也觉得,即使在List 的情况下使用它也不方便)。我们要写一个类的方法,把listItems 里的每一个元素全都返回。同时我们要把do{}while()改动。下面是涉及到改动的全部代码:
+展开
-ActionScript
public function getShowAllItemRenderers():ArrayCollection{
var returnArry:ArrayCollection = new ArrayCollection()
for each (var i:* in listItems){
for each (var j:* in i){ returnArry.addItem(j); }
}
return returnArry;
}
public function set rendererStyle(styleName:String):void {
_rendererStyle = styleName;
if ( collection != null ) {
var i:int = 0;
var temp :ArrayCollection = getShowAllItemRenderers();
for each (var item:* in temp){
if (item.styleName != _rendererStyle){
item.styleName = _rendererStyle;
item.invalidateDisplayList();
else { continue ; }
}
}
}

上面这两个方法,放到List 里,就是我们在前边说的第二种解决办法。

我们使用一个按钮控制itemRenderer 样式的改变:
+展开
-XML
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxmlwidth="400height="700"
xmlns:cookbook="oreilly.cookbook.*">

<mx:Style>
.firstStyle{ color:#999999; }
.secondStyle{ color:#3344ff; }

</mx:Style>
<mx:Button toggle="trueclick="list.rendererStyle == 'firstStyle' ? list.rendererStyle='secondStyle' : list.rendererStyle='firstStylelabel="TOGGLE" />
<cookbook:StylingRendererList id="listdataProvider="{oreilly.cookbook.DataHolder.simpleArray}"
rendererStyle="firstStylewidth="200"/>

</mx:VBox>

最后补充一下,原文中的做法只能把当前的renderer 全部上色,如果用户拖滚动条,页面上就乱了。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表