Egret社区

Egret框架GUI教程 - 列表

2014-9-10 20:29
1429916
本帖最后由 guoshaorui 于 2014-9-10 20:33 编辑

列表组件(egret.gui.List),实际上是和上一节所述的DataGroup密不可分的。它和DataGroup的区别在于:

  • List是可以定制皮肤的,DataGroup没有皮肤
  • 在默认提供的两套主题中,已经为List设置了默认的皮肤,实现常规的垂直布局,且具备滚动功能
  • List拥有labelField和labelFunction,可以更灵活的定义文本显示
  • 在List中选中一项,会触发change事件,然后您就可以执行后续的逻辑处理
  • 可以设置List中的默认选项

来看看List的基本用法:
[mw_shl_code=actionscript3,true]//先创建一个数组
var sourceArr:any[] = [];
for (var i:number = 1; i < 50; i++)
{
    sourceArr.push({name:"item"+i});
}
//用ArrayCollection包装
var myCollection:egret.gui.ArrayCollection = new egret.gui.ArrayCollection(sourceArr);
//创建列表
var dataList:egret.gui.List = new egret.gui.List();
dataList.dataProvider = myCollection;
dataList.percentWidth = 100;
dataList.percentHeight = 100;
this.addElement(dataList);[/mw_shl_code]
因为已经设置了默认皮肤,所以即使我们不在代码中设置皮肤,列表也是可以显示出来的:
list1.png
可以看到列表中文本显示的是"Object",因为我们的数据上,没有"label"属性,而是一个"name"属性,所以列表不知道应该取哪个属性作为显示文本。这里需要您用labelField或labelFunction显式定义一下:
[mw_shl_code=actionscript3,true]//二选一,如果同时定义,以labelFunction为准
dataList.labelField = "name";
//dataList.labelFunction = this.myLabelFunction;
/**labelFunction*/
private myLabelFunction(item:any):string {
    return "Hi:"+item.name;
}[/mw_shl_code]
list2.png
您可以设置,默认选中哪一项:
[mw_shl_code=actionscript3,true]//默认选项
dataList.selectedIndex = 1;
dataList.selectedItem = myCollection.getItemAt(2);//索引从0开始计算,所以2代表第三项数据[/mw_shl_code]
如果用户改变了选项,您也是可以通过selectedIndex和selectedItem来获取的

list3.png
事件
在列表中,您可以侦听change事件,了解选项的变化:
[mw_shl_code=actionscript3,true]dataList.addEventListener(egret.gui.IndexChangeEvent.CHANGE,this.listChangeHandler,this);
/**事件侦听*/
private listChangeHandler(evt:egret.Event):void {
    var dataList:egret.gui.List = evt.currentTarget;
    console.log(evt.oldIndex+","+evt.newIndex);
    console.log(dataList.selectedItem.name);
}[/mw_shl_code]
list4.png
注意change事件是必须选项改变后才会被触发的,如果您想要的是点击即触发(选项不一定改变),可以侦听itemClick事件:
[mw_shl_code=actionscript3,true]dataList.addEventListener(egret.gui.ListEvent.ITEM_CLICK,this.listClickhandler,this);
/**事件侦听*/
private listClickhandler(evt:egret.gui.ListEvent):void {
    var dataList:egret.gui.List = evt.currentTarget;
    console.log(evt.item.name+" clicked");
}[/mw_shl_code]
另外您还可以侦听IndexChangeEvent.CHANGING事件,这个事件派发在选项即将改变之前,如果您想阻止选项的发生,可以这样做:
[mw_shl_code=actionscript3,true]dataList.addEventListener(egret.gui.IndexChangeEvent.CHANGING,this.listChangingHandler,this);
/**事件侦听*/
private listChangingHandler(evt:egret.gui.IndexChangeEvent):void {
    if(evt.newIndex==2) {
        evt.preventDefault();
    }
}[/mw_shl_code]
大数据优化
List有一个useVirtualLayout属性,默认是true,这个属性决定了列表创建内部对象的策略:
  • 策略1 useVirtualLayout=false,有多少数据就创建多少个ItemRenderer的实例
  • 策略2 useVirtualLayout=true,判断组件尺寸,确定同时能显示的最大个数,根据这个数字来创建一组ItemRenderer并循环利用,当您滚动切换数据的时候,只是这一组ItemRenderer循环切换自己的位置和显示,只要计算得当,这个过程将是无缝衔接且顺畅的。
显然策略2在数据量大的时候,会具备更好的性能优势。假设您有上千条数据,用策略1的话,界面估计就卡死了,而用策略2的话就不存在这个问题,因为数据量的增加,并不会导致显示对象数量的增加,也不会导致重绘次数的增加。
对比图(1000条数据):
list5.png list6.png
但您也要注意,在开启useVirtualLayout的情况下,数据的数量和ItemRenderer的数量的不对等的。所以这种情况下,你在list.contentGroup.getElementAt(index)上获取的对象,不一定和对应索引的数据相符合。如果您需要根据索引,获取一个ItemRenderer的实例,就必须注意这一点。
皮肤
如果您使用了simple主题,那默认的列表皮肤是skins.simple.ListSkin,来看看这个Skin里都包含了哪些内容:
[mw_shl_code=xml,true]//ListSkin.exml
<e:Skin xmlns:e="http://ns.egret-labs.org/egret" xmlns:w="http://ns.egret-labs.org/wing">
    <e:Scroller width="100%" height="100%">
        <eataGroup id="dataGroup" itemRendererSkinName="skins.simple.ItemRendererSkin">
            <e:layout>
                <e:VerticalLayout gap="0" horizontalAlign="contentJustify" />
            </e:layout>
        </eataGroup>
    </e:Scroller>
</e:Skin>[/mw_shl_code]
这个皮肤是用exml描述的,但应该不难理解。里面呢,就是一个Scroller包含一个DataGroup,结构很简单。如果翻译成对等的TS文件,应该是这样:
[mw_shl_code=actionscript3,true]module skins.simple
{
    export class ListSkin extends egret.gui.Skin
    {
        /**和组件中的定义相对应,确定皮肤应该具备哪些部件*/
        public skinParts:Array<string> = ["dataGroup"];
        /**对于列表组件来说,dataGroup是必须有的*/
        public dataGroup:egret.gui.DataGroup;

        public constructor() {
            super();
        }
        public createChildren(): void {
            super.createChildren();
            //dataGroup必须有
            var scroller:egret.gui.Scroller = new egret.gui.Scroller();
            scroller.percentWidth = 100;
            scroller.percentHeight = 100;
            this.addElement(scroller);
            this.dataGroup = new egret.gui.DataGroup();
            this.dataGroup.itemRendererSkinName = "skins.simple.ItemRendererSkin";
            var vLayout:egret.gui.VerticalLayout = new egret.gui.VerticalLayout();
            vLayout.gap = 0;
            vLayout.horizontalAlign = egret.HorizontalAlign.CONTENT_JUSTIFY;
            this.dataGroup.layout = vLayout;
            scroller.viewport = this.dataGroup;
        }
    }
}[/mw_shl_code]
可以看出exml的描述相当简洁(在功能对等的情况下),所以在实际的皮肤制作中,我们也应该尽量采取exml。

我们来扩展一下这个列表皮肤,增加一个背景显示(BgListSkin.exml):
[mw_shl_code=xml,true]<e:Skin xmlns:e="http://ns.egret-labs.org/egret" xmlns:w="http://ns.egret-labs.org/wing">
    <e:UIAsset id="bg" width="100%" height="100%"
               source="app_egret_labs_jpg"/>
    <e:Scroller top="20" bottom="20" left="20" right="20" horizontalScrollPolicy="off">
        <eataGroup id="dataGroup" itemRendererSkinName="skins.simple.ItemRendererSkin">
            <e:layout>
                <e:VerticalLayout gap="0" horizontalAlign="contentJustify" />
            </e:layout>
        </eataGroup>
    </e:Scroller>
</e:Skin>[/mw_shl_code]
然后设置列表的皮肤为我们扩展的这个:
[mw_shl_code=actionscript3,true]dataList.skinName = "uiskins.BgListSkin";[/mw_shl_code]
效果:
list7.png
自定义ItemRenderer
默认的ItemRenderer和ItemRendererSkin只是显示一个文本,如果我们要增加更复杂的功能,比如显示一个开关按钮,就要自己去编写ItemRenderer和ItemRendererSkin了。
先编写一个有ToggleButton的ItemRenderer:
[mw_shl_code=actionscript3,true]module uiskins
{
    export class ToggleRenderer extends egret.gui.ItemRenderer
    {
        public labelDisplay:egret.gui.Label;
        public toggleButton:egret.gui.ToggleButton;

        public constructor(){
            super();
            this.touchChildren = true;
        }
        public dataChanged():void{
            this.labelDisplay.text = this.data.name;
            this.toggleButton.selected = this.data.checked;
            this.toggleButton.addEventListener(egret.Event.CHANGE,this.toggleChangeHandler,this);
            this.toggleButton.addEventListener(egret.TouchEvent.TOUCH_BEGIN,this.toggleTouchHandler,this)
        }
        /**取消事件的传递,避免按钮操作影响列表操作*/
        private toggleTouchHandler(evt:egret.Event):void {
            evt.stopImmediatePropagation();
        }
        /**将按钮的操作映射到数据上*/
        private toggleChangeHandler(evt:egret.Event):void {
            this.data.checked = this.toggleButton.selected;
        }
    }
}[/mw_shl_code]
然后定义这个ItemRenderer的皮肤(ToggleRendererSkin.exml):
[mw_shl_code=xml,true]<e:Skin xmlns:e="http://ns.egret-labs.org/egret" xmlns:w="http://ns.egret-labs.org/wing"
        height="80">
    <e:states>
        <e:State name="up" />
        <e:State name="down" />
        <e:State name="disabled" />
    </e:states>
    <e:UIAsset width="100%" height="100%"
               source.up="button_normal_png"
               source.down="button_down_png"
               source.disabled="button_disabled_png" />
    <eabel id="labelDisplay"
             size="24"
             fontFamily="Tahoma"
             width="100%"
             height="100%"
             textColor="0x111111"
             textColor.down="0xffffff"
             textColor.disabled="0xcccccc"
             textAlign="left"
             paddingLeft="20"
             verticalAlign="middle" />
    <e:ToggleButton id="toggleButton"
                    hostComponentKey="ToggleOnOffButton"
                    right="30"
                    verticalCenter="0" />
</e:Skin>[/mw_shl_code]
效果:
list8.png
如果需要为同一个列表指定不同的ItemRenderer,您可以使用itemRendererFunction,示例:
[mw_shl_code=actionscript3,true]dataList.itemRendererFunction = this.myItemRendererFunction;

private fact1:egret.gui.ClassFactory = new egret.gui.ClassFactory(ItemRendererClass1);
private fact2:egret.gui.ClassFactory = new egret.gui.ClassFactory(ItemRendererClass2);
private myItemRendererFunction(item:any):IFactory {
    if(item.type==1) {
        return this.fact1;
    } else {
        return this.fact2;
    }
}[/mw_shl_code]

分享到 :
5 人收藏

16 个回复

倒序浏览
codefish  圆转纯熟 | 2014-10-5 21:27:39
学习了,mark下
langlive  登堂入室 | 2014-11-22 19:51:01
求打包的源码
liuyun  初学乍练 | 2014-12-5 15:31:28
可以添加图片?
guoshaorui  超级斑竹 | 2014-12-8 10:37:54

可以,在ItemRenderer的Skin里添加就行了
sz1992  初学乍练 | 2015-1-30 15:09:05
学习一下
jay1230001  登堂入室 | 2015-1-31 13:46:38
哇咔咔哇咔咔哇咔咔哇咔咔哇咔咔哇咔咔
derek6616  初窥堂奥 | 2015-3-29 01:03:23
快速扫过!~~~~~
fangshengfu  登堂入室 | 2015-5-11 18:19:33
this.labelDisplay.text = this.data.name;
this.toggleButton.selected = this.data.checked;

请问左边的name checked这个值是从什么地方来的,麻烦大神回复一下!!!
moon_release  登堂入室 | 2015-5-18 23:50:57
fangshengfu 发表于 2015-5-11 18:19
this.labelDisplay.text = this.data.name;
this.toggleButton.selected = this.data.checked;

同问 你解决了吗
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|京网文[2014]0791-191号|京ICP证150115号|Egret社区 ( 京ICP备14025619号 )

Powered by Discuz! X3.4 © 2001-2019 Comsenz Inc.

返回顶部