屏幕的尺寸总是有限的,当内容已经超出屏幕的范围时,需要特殊的处理方式。EUI 利用滚动条实现此功能。通过拉动滚动条,可以在有限的屏幕中浏览全部内容。在其他引擎中我们可以使用TableView或者ScrollView来实现这些效果,在Egret中怎么使用呢,有两个办法实现。
一、使用滚动控制容器(Scroller)
二、使用滚动控制容器(Scroller) + 列表组合的方式(List)
还对Egret的Scroller不太了解的 点这里
对Egret的List不太了解的 点这里
不过你看完了官网中的示例和介绍,如果想了解更深的话,请继续往下看。
上面的第一种方式,是直接将视图添加到Scroller的ViewPort中,这种方式非常不建议这么做,原因是太浪费性能了,当你的数据量比较大的时候,在微信小程序上面肯定会卡的。(我已经尝试过了,所以我只将第二种做法和其中遇到的问题记录下来,供大家参考交流)
先看一下效果:
显示示例中有几个比较重要的部分
一、顶部显示下面cell的分类属性
二、右部滑动条
三、list中cell刷新时数据更新
下面,我们一个一个的解决上面的问题
问题一:
先看我们的布局
上面空的部分是做的一个导航器,必须要为空,Scroller大小就是演示图中所有滑动部分,在上面添加一个Group覆盖到Scroller顶部
好的,其实你也可以想到这样做,这很简单,关键的是这样做了会引起第二个问题,右部滑动条在顶部的时候会被group遮挡,哈哈,我们直接进入到第二个问题吧。
问题二:
其实要解决问题二很简单的,直接添加一个VScrollerBar组件不就可以了嘛。当然可以这样就可以了,不过这样之后又怎么办呢,Scroller滑动时怎么在滑动条上面表现出来呢。
将一下代码添加到初始化的地方,新建GameLobbyView.ts
private init() {
this.game_list_scroller.verticalScrollBar = this.verticalScrollBar;
this.game_list_scroller.scrollPolicyH = eui.ScrollPolicy.OFF;
this.game_list_scroller.addEventListener(egret.Event.CHANGE, this.onScrollerChange, this);
}
/**
* 滚动位置改变的时候
*/
private onScrollerChange() {
let scrollV = this.game_list_scroller.viewport.scrollV;
let thumbY = Math.abs(scrollV) / (this.game_list_scroller.viewport.measuredHeight - this.game_list_scroller.height);
this.verticalScrollBar.thumb.y = thumbY * (this.verticalScrollBar.height - this.verticalScrollBar.thumb.height);
}
在onScrollerChange中监听Scroller的滑动,并自己计算滑动条的位置。
问题三:
数据刷新问题,这才是重点,我们一步一步来吧。
1、新建ts文件对应Scroller中的单个cell视图GameCellView
类,并建立对应皮肤GameCellViewSkin.exml
,同时新建数据结构GameCellData
类,保存cell中需要使用到的数据
class GameCellData {
***
}
class GameCellView extends eui.Component implements eui.IItemRenderer {
// ------------- 接口必须实现数据 ----------------
private _data: GameCellData;
public set data(data: GameCellData) {
this._data = data;
this.updateView(this._data);
}
public get data(): GameCellData {
return this._data;
}
public selected: boolean;
public itemIndex: number;
// ------------- 接口必须实现数据 ----------------
}
上面GameCellView
必须实现eui.IItemRenderer
接口,
/**
* 列表类组件的项呈示器接口。
*/
interface IItemRenderer extends UIComponent {
/**
* 要呈示或编辑的数据。
*/
data: any;
/**
* 如果项呈示器可以将其自身显示为已选中,则为 true。
*/
selected: boolean;
/**
* 项呈示器的数据提供程序中的项目索引。
*/
itemIndex: number;
}
2、在主类GameLobbyView.ts
中初始化list数据
private initList() {
this.game_list.itemRenderer = GameCellView;
let listData = new eui.ArrayCollection(this.gameCellDatas);
this.game_list.dataProvider = listData;
this.game_list_scroller.viewport = this.game_list;
// 以上代码在官网中你应该有看见过
// 重要的下面的代码
this.game_list.updateRenderer = this.gameCell.bind(this);
}
/**
* List更新数据是调用
*/
private gameCell(renderer: eui.IItemRenderer, itemIndex: number, data: any) : eui.IItemRenderer{
renderer.data = data;
renderer.itemIndex = itemIndex;
return renderer;
}
先看一下updateRenderer接口说明,所以我们需要实现一个更深数据的接口,上面实现在gameCell中。
/**
* 更新项呈示器,以备使用或重用
*/
updateRenderer(renderer: IItemRenderer, itemIndex: number, data: any): IItemRenderer;
当刷新视图的时候,会回调gameCell函数,我们在gameCell中给视图设置数据。
3、在GameCellView
中实现视图刷新数据设置功能
/**
* 更新视图
*/
private updateView(data: GameCellData) {
// your code
}
public myOperate() {
console.log("外界操作视图");
}
这里就是你的代码逻辑了,自己更新视图逻辑。
4、假如我想在GameLobbyView.ts
对GameCellView操作怎么办呢。
这里有两个办法,
a、重新刷新list的Data的数据,但是这样相当于重置了数据,不建议使用
b、更改传给list的原数据,并对gameCellView对象操作。
/**
* 折叠所有同类的游戏
*/
private foldAllGamesByAbilityId(abilityId) {
let games = gameAbility[abilityId];
// 更新数据
for(let gameId of games) {
let gameData = this.gameInfoDatas[gameId] as GameCellData;
// gameData更改
}
// 更新视图
for(let i = 0; i < this.game_list.numChildren; i++) {
let gameCellView = this.game_list.getChildAt(i) as GameCellView;
gameCellView.myOperate();
}
}
上面只是想说我们可以拿到List中的视图对象,虽然我们并没有用代码创建视图对象,但是List自动帮我们创建了。
上面只是说了思路,已经很完整了,详细你看完后就知道怎么做了。