学习LayaBox已经有10多天了,今天在LayaBox官网教程中学习到了如何制作自定义List,结合自己的理解,便记录下制作过程。本编文章纯属个人学习笔记,如果有什么理解错误的地方还望大神指教指教~~
1.制作List的UI面板
在LayaAir编辑器的设计模式下新建一个名为CustomListDemo的ui页面,页面大小为750*1334,因为这个List以iphone6为测试机型的例子,如图(1)所示:
图(1)新建list的ui页面
拉入自己准备好的图片资源作为List单元格的背景,并且点击左侧属性面板的grid按钮选项设置图片的九宫格,最后设置该组件的x=0,y=0,width=750,height=80,如图(2)所示:
图(2)添加单元格背景
拉入两个Label组件,分别作为显示单元格的序号和内容,在设置好自己心仪的样式后,把作为显示单元格序号的Label组件的name属性值设置为listNumber,作用是为了可以在代码中通过getChildByName()函数获取该Label对象,如图(3)所示:
图(3)添加Label
在资源管理器的资源项中拉入一个checkBox.png,作为单元格多选框。如果该组件太小的话,因为该组件的资源其实也是一张图片,所以可以在左侧的属性面板中修改scaleX,scaleY两项来改变组件的大小,最后同上述的Label一样,将name属性的值设置为check,作用是为了可以在代码中通过getChildByName()函数获取该Label对象。如图(4)所示:
图(4)添加checkBox
选中view中所有的组件,按下ctrl+b,转换为Box容器,之后再在左侧的属性面板中设置Box容器的渲染类型(renderType=render),如图(5)、图(6)所示:
图(5)转换为Box容器
图(7)设置渲染类型
选中刚刚转换的Box,按下ctrl+b,转换为List。根据LayaBox的官方解释,所有的List容器,必须是基于Box容器的,list基于box循环产生。通过下图我们可以直观List的层级关系,如图(7)、图(8)所示:
图(7)转换为List
图(8)List的层级关系
在资源管理器的资源项中拉入两个Button.png,分别作为增加、删除按钮,在设置好自己心仪的样式后,在左侧的属性面板中将var属性的值分别设置为_add、_del,这样做是为了可以在代码中通过“对象引用+.”获取到该对象,如图(9)所示:
图(9)添加两个Button
将Box的height设置为与背景图一样的高度,然后根据实际情况在List将repeatY属性的值设置为15(repeatX表示X轴的单元格数量,repeatY表示Y轴的单元格数量),接着将List的var属性的值设置为_list,作用是为了可以在代码中通过“对象引用+.”获取到该对象,如图(11)、图(12)所示:
图(11)设置Box的height
图(12)设置List的repeatY
完成后按ctrl+F12发布。
最后在代码上测试。我是习惯在Flas Builder上编写代码的,新建一个名为TestCustomListDemo的ActionScript类,在该类中编写如下代码:
package
{
import laya.display.Stage;
import laya.utils.Handler;
import laya.webgl.WebGL;
import ui.ListPageUI;
public class TestCustomListDemo
{
private var listUI:ListPageUI; // ListUI面板
private var arr:Array; // list的数据数组
public function TestCustomListDemo()
{
Laya.init(750, 1334, WebGL); // 舞台大小-600*1000
Laya.stage.bgColor = '#ffffff'; // 舞台背景-白色
Laya.stage.scaleMode = Stage.SCALE_SHOWALL; // 设置舞台适配模式-showall(按最小比例缩放)
Laya.loader.load(["res/atlas/listPage.atlas", "res/atlas/comp.atlas"],
Handler.create(this, onLoaded)); // 加载图片资源
}
/**
* 加载资源回调函数
*/
private function onLoaded():void
{
listUI = new ListPageUI(); // 实例化ListUI面板
Laya.stage.addChild(listUI); // 将ListUI面板添加到舞台中
}
}
}
运行效果如图(13)所示:
图(13)
因为还没有给List设置列表数据源,所以会显示出刚刚在LayaAir中设置的repeatY的数量的单元格。
2.完成最初的渲染工作
在onLoad()回调函数中添加如下代码,渲染时需要用到单元格渲染器renderHandle()以及“laya.display.Node ”API下通过子节点名字获取子节点对象的方法getChildByName,如下所示:
package
{
import laya.display.Stage;
import laya.ui.Box;
import laya.ui.CheckBox;
import laya.ui.Label;
import laya.utils.Handler;
import laya.webgl.WebGL;
import ui.ListPageUI;
public class TestCustomListDemo
{
private var listUI:ListPageUI; // ListUI面板
private var arr:Array; // list的数据数组
public function TestCustomListDemo()
{
Laya.init(750, 1334, WebGL); // 舞台大小-600*1000
Laya.stage.bgColor = '#ffffff'; // 舞台背景-白色
Laya.stage.scaleMode = Stage.SCALE_SHOWALL; // 设置舞台适配模式-showall(按最小比例缩放)
Laya.loader.load(["res/atlas/listPage.atlas", "res/atlas/comp.atlas"],
Handler.create(this, onLoaded)); // 加载图片资源
}
/**
* 加载资源回调函数
*/
private function onLoaded():void
{
listUI = new ListPageUI(); // 实例化ListUI面板
Laya.stage.addChild(listUI); // 将ListUI面板添加到舞台中
arr = []; //数据数组初始化为
listUI._list.array = arr; // 为list赋值数据数组
listUI._list.vScrollBarSkin = ""; // 添加滚动功能(UI不可显示)
listUI._list.renderHandler = new Handler(this, onRender); // 渲染list
}
/**
* 渲染list
*/
private function onRender(cell:Box, index:int):void
{
var data:Object = arr[index]; // 获取源数据
var listNumber:Label = cell.getChildByName("listNumber") as Label; // 获取Box中的Label标签
listNumber.text = data.listNumber.text; // 给标签设置序号
}
}
}
由于List的数据源数组 arr 中为空,也即没有数据,所以运行之后没有单元格显示,如图(14)所示:
图(14)
3.完成添加功能
现List增加,需要用到LayaAir引擎laya.display.Sprite中的事件侦听on()方法对鼠标点击事件CLICK进行侦听,以及laya.ui.List API中添加单元格数据源的方法addItem(),代码如下:
package
{
import laya.display.Stage;
import laya.events.Event;
import laya.ui.Box;
import laya.ui.CheckBox;
import laya.ui.Label;
import laya.utils.Handler;
import laya.webgl.WebGL;
import ui.ListPageUI;
public class TestCustomListDemo
{
private var listUI:ListPageUI; // ListUI面板
private var arr:Array; // list的数据数组
public function TestCustomListDemo()
{
Laya.init(750, 1334, WebGL); // 舞台大小-600*1000
Laya.stage.bgColor = '#ffffff'; // 舞台背景-白色
Laya.stage.scaleMode = Stage.SCALE_SHOWALL; // 设置舞台适配模式-showall(按最小比例缩放)
Laya.loader.load(["res/atlas/listPage.atlas", "res/atlas/comp.atlas"],
Handler.create(this, onLoaded)); // 加载图片资源
}
/**
* 加载资源回调函数
*/
private function onLoaded():void
{
listUI = new ListPageUI(); // 实例化ListUI面板
Laya.stage.addChild(listUI); // 将ListUI面板添加到舞台中
arr = []; //数据数组初始化为
listUI._list.array = arr; // 为list赋值数据数组
listUI._list.vScrollBarSkin = ""; // 添加滚动功能(UI不可显示)
listUI._list.renderHandler = new Handler(this, onRender); // 渲染list
listUI._add.on(Event.CLICK, this, onClickAdd); // 增加按钮侦听事件
}
/**
* 增加按钮侦听事件
*/
private function onClickAdd():void
{
listUI._list.addItem({listNumber:{text:arr.length+1}});
}
/**
* 渲染list
*/
private function onRender(cell:Box, index:int):void
{
var data:Object = arr[index]; // 获取源数据
var listNumber:Label = cell.getChildByName("listNumber") as Label; // 获取Box中的Label标签
listNumber.text = data.listNumber.text; // 给标签设置序号
}
}
}
运行结果如图(15)所示:
图(15)
4.完成复选框功能
需要用到单元格鼠标事件处理器mouseHandle,同时需要修改onClickAdd()函数中传入addItem()函数的数据格式、render()函数中渲染List时的数据,添加一个isCheck键值对(true表示已选中,false表示为选中),这样做是为了实时更新复选框的状态以及为接下来的删除功能做准备,代码如下:
package
{
import laya.display.Stage;
import laya.events.Event;
import laya.ui.Box;
import laya.ui.CheckBox;
import laya.ui.Label;
import laya.utils.Handler;
import laya.webgl.WebGL;
import ui.CustomListDemoUI;
public class TestCustomListDemo
{
private var listUI:CustomListDemoUI; // ListUI面板
private var arr:Array; // list的数据数组
public function TestCustomListDemo()
{
Laya.init(750, 1334, WebGL); // 舞台大小-600*1000
Laya.stage.bgColor = '#ffffff'; // 舞台背景-白色
Laya.stage.scaleMode = Stage.SCALE_SHOWALL; // 设置舞台适配模式-showall(按最小比例缩放)
Laya.loader.load(["res/atlas/listPage.atlas", "res/atlas/comp.atlas"],
Handler.create(this, onLoaded)); // 加载图片资源
}
/**
* 加载资源回调函数
*/
private function onLoaded():void
{
listUI = new CustomListDemoUI(); // 实例化ListUI面板
Laya.stage.addChild(listUI); // 将ListUI面板添加到舞台中
arr = []; //数据数组初始化为
listUI._list.array = arr; // 为list赋值数据数组
listUI._list.vScrollBarSkin = ""; // 添加滚动功能(UI不可显示)
listUI._list.renderHandler = new Handler(this, onRender); // 渲染list
listUI._list.mouseHandler = new Handler(this, onMouse); // 渲染list单元格鼠标点击事件
listUI._add.on(Event.CLICK, this, onClickAdd); // 增加按钮侦听事件
}
/**
* list单元格鼠标点击事件
*/
private function onMouse(e:Event, number:int):void
{
// 事件类型是否为Event.CLICK
if (e.type == Event.CLICK)
{
// 点中目标是否为CheckBox
if (e.target is CheckBox )
{
var temp:Object = arr[number];
// 点中的CheckBox是否选中状态
if ((e.target as CheckBox).selected)
{
listUI._list.setItem(number, {listNumber:{text:temp.listNumber.text}, isCheck:true});
}
else
{
listUI._list.setItem(number, {listNumber:{text:temp.listNumber.text}, isCheck:false});
}
}
}
}
/**
* 增加按钮侦听事件
*/
private function onClickAdd():void
{
listUI._list.addItem({listNumber:{text:arr.length+1, isCheck:false}});
}
/**
* 渲染list
*/
private function onRender(cell:Box, index:int):void
{
var data:Object = arr[index]; // 获取源数据
var listNumber:Label = cell.getChildByName("listNumber") as Label; // 获取Box中的Label标签
listNumber.text = data.listNumber.text; // 给标签设置序号
var checkBox:CheckBox = cell.getChildByName("check") as CheckBox; // 获取复选框
// y用数组中的check值来标志复选框是否选中
if (data.isCheck)
{
checkBox.selected = true;
}
else
{
checkBox.selected = false;
}
}
}
}
运行结果如图(16)所示:
图(16)
5.完成删除功能
完成删除功能,需要用到LayaAir引擎laya.display.Sprite中的事件侦听on()方法对鼠标点击事件CLICK进行侦听,注意还需要用到refresh()函数刷新列表数据源,代码如下:
package
{
import laya.display.Stage;
import laya.events.Event;
import laya.ui.Box;
import laya.ui.CheckBox;
import laya.ui.Label;
import laya.utils.Handler;
import laya.webgl.WebGL;
import ui.CustomListDemoUI;
public class TestCustomListDemo
{
private var listUI:CustomListDemoUI; // ListUI面板
private var arr:Array; // list的数据数组
public function TestCustomListDemo()
{
Laya.init(750, 1334, WebGL); // 舞台大小-600*1000
Laya.stage.bgColor = '#ffffff'; // 舞台背景-白色
Laya.stage.scaleMode = Stage.SCALE_SHOWALL; // 设置舞台适配模式-showall(按最小比例缩放)
Laya.loader.load(["res/atlas/listPage.atlas", "res/atlas/comp.atlas"],
Handler.create(this, onLoaded)); // 加载图片资源
}
/**
* 加载资源回调函数
*/
private function onLoaded():void
{
listUI = new CustomListDemoUI(); // 实例化ListUI面板
Laya.stage.addChild(listUI); // 将ListUI面板添加到舞台中
arr = []; //数据数组初始化为
listUI._list.array = arr; // 为list赋值数据数组
listUI._list.vScrollBarSkin = ""; // 添加滚动功能(UI不可显示)
listUI._list.renderHandler = new Handler(this, onRender); // 渲染list
listUI._list.mouseHandler = new Handler(this, onMouse); // 渲染list单元格鼠标点击事件
listUI._add.on(Event.CLICK, this, onClickAdd); // 增加按钮侦听事件
listUI._del.on(Event.CLICK, this, onClickDel); // 删除按钮侦听事件
}
/**
* 删除按钮侦听事件
*/
private function onClickDel():void
{
var tempArr:Array = []; // 临时数组
for (var i:int = 0; i < arr.length; i++)
{
// 如果未呈选中状态,则push进临时数组保存起来
if (!arr[i].isCheck)
{
tempArr.push(arr[i]);
}
}
arr = tempArr; // 将保存下来的数组赋值给原来的数组
listUI._list.array = tempArr; // 更改数据源
listUI._list.refresh(); // 刷新列表数据源
}
/**
* list单元格鼠标点击事件
*/
private function onMouse(e:Event, number:int):void
{
// 事件类型是否为Event.CLICK
if (e.type == Event.CLICK)
{
// 点中目标是否为CheckBox
if (e.target is CheckBox )
{
var temp:Object = arr[number];
// 点中的CheckBox是否选中状态
if ((e.target as CheckBox).selected)
{
listUI._list.setItem(number, {listNumber:{text:temp.listNumber.text}, isCheck:true});
}
else
{
listUI._list.setItem(number, {listNumber:{text:temp.listNumber.text}, isCheck:false});
}
}
}
}
/**
* 增加按钮侦听事件
*/
private function onClickAdd():void
{
listUI._list.addItem({listNumber:{text:arr.length+1, isCheck:false}});
}
/**
* 渲染list
*/
private function onRender(cell:Box, index:int):void
{
var data:Object = arr[index]; // 获取源数据
var listNumber:Label = cell.getChildByName("listNumber") as Label; // 获取Box中的Label标签
listNumber.text = data.listNumber.text; // 给标签设置序号
var checkBox:CheckBox = cell.getChildByName("check") as CheckBox; // 获取复选框
// y用数组中的check值来标志复选框是否选中
if (data.isCheck)
{
checkBox.selected = true;
}
else
{
checkBox.selected = false;
}
}
}
}
运行结果如图(17)所示:
图(17)
6.总结
在学习制作自定义List的过程中,有两个致命的地方浪费了我很多时间:
- 忘记设置List的repeatY的值,导致无法显示添加之后的新单元格,我以为是代码错了,将教程中的代码复制运行一遍也还是不行。
- 不理解渲染到底是干嘛的,经过再三琢磨之后,大概可以理解为:初始化List的数据源,例如在render()函数中,将List的子组件Label的值设置为列表数组中的ListNumber.text的值,这在之后调用addItem()是传入的数据源中,List自动获取ListNumber.text的值并复制给Label;
知识梳理: - 所有的List容器,必须是基于Box容器的,list基于box循环产生。
- 渲染 ≈ 设置逻辑或者说定义数据赋值原则。
- 嵌套生成的组件可以通过laya.display.Node ”API的getChildByName函数获取子组件对象。
- 更改List的数据后需要调用refresh()函数刷新列表数据源
.....








网友评论