Egret 滚动控制容器(Scroller)和列表List

发表于2018-11-13
评论0 5k浏览
屏幕的尺寸总是有限的,当内容已经超出屏幕的范围时,需要特殊的处理方式。EUI 利用滚动条实现此功能。通过拉动滚动条,可以在有限的屏幕中浏览全部内容。在其他引擎中我们可以使用TableView或者ScrollView来实现这些效果,在Egret中怎么使用呢,有两个办法实现。

1.使用滚动控制容器(Scroller)
2.使用滚动控制容器(Scroller) + 列表组合的方式(List)

Egret的Scroller介绍:http://developer.egret.com/cn/github/egret-docs/extension/EUI/container/scroller/index.html
gret的List介绍:http://developer.egret.com/cn/github/egret-docs/extension/EUI/dataCollection/list/index.html

不过你看完了官网中的示例和介绍,如果想了解更深的话,请继续往下看。

上面的第一种方式,是直接将视图添加到Scroller的ViewPort中,这种方式非常不建议这么做,原因是太浪费性能了,当你的数据量比较大的时候,在微信小程序上面肯定会卡的。(我已经尝试过了,所以我只将第二种做法和其中遇到的问题记录下来,供大家参考交流)

先看一下效果:


显示示例中有几个比较重要的部分:
1. 顶部显示下面cell的分类属性
2. 右部滑动条
3. 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操作怎么办呢。

这里有两个办法:
  • 重新刷新list的Data的数据,但是这样相当于重置了数据,不建议使用
  • 更改传给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自动帮我们创建了。
上面只是说了思路,已经很完整了,详细你看完后就知道怎么做了。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引