参照iOS的UITAbleView 写了个列表

/*

 * @Author: xiao yuwen

 * @Description: UITableView

 * 

 * 

 * 使用

 * 1、新建一个scrollView 创建tableView

 * let scrollView = cc.find("scrollView", this.node).getComponnent(cc.ScrollView)

 * let content = cc.find("content", scrollView.node);

 * this.tableView = new UITableView();

 * this.tableView.init(scrollView, content)

 * 

 * scrollview 的horizontal vertical 参数来控制水平或者垂直滑动,只支持一个方向的滑动

 * 

 * 将UItableViewCell组件加到cell节点上

 * 

 * 2、设置代理

 * this.tableView.delegate = this;

 * this.tableView.dataSorces = this; 

 * 

 * 3、实现代理方法  UITableDataSources, UITableViewDelegate

 * 

 * 4、刷新数据

 * this.tableView.reloadData(); 

 * 

 * 5、清除缓存

 * this.tableView.destroy();

 * 

 * 

 */

import UITableViewCell from "./UITableViewCell";

import { Dictionary } from '../../../common/data_structure/Dictionary';

type numberOfRowInTable     = (tableView: UITableView) => number;

type tableCellAtIndex       = (tableView: UITableView, idx: number) => cc.Node;

type tableCellTypeAtIndex   = (tableView: UITableView, idx: number) => string;

type tableCellSizeAtIndex   = (tableView: UITableView, idx: number) => cc.Size;

type tableCellHeightAtIndex = (tableView: UITableView, idx: number) => number;

type didSelectRowAtIndex    = (tableView: UITableView, idx: number) => void;

export enum Direction {

    horizontal  = 0, //水平

    vertical    = 1, //竖直

}

export interface UITableViewDelegate {

    /**

     * @description: 点击回调

     * @param {*}

     * @return {*}

     */    

     didSelectRowAtIndex?(tableView: UITableView, idx: number): void;

     scrollViewDidScroll?(tableview: UITableView, offset: number): void;

}

export interface UITableDataSources {

    

    /**

     * @description: tableView的cell数量

     * @param {*}

     * @return {*}

     */    

    numberOfRowInTable(tableView: UITableView): number;

    /**

     * @description: tableViewCell

     * @param {*}

     * @return {*}

     */    

    tableCellAtIndex(tableView: UITableView, idx: number): cc.Node;

    /**

     * @description: tableViewCell的高度

     * @param {*}

     * @return {*}

     */  

    tableCellHeightAtIndex(tableView: UITableView, idx: number): number;  

    /**

     * @description: tableViewCell的类型

     * @param {*}

     * @return {*}

     */    

    tableCellTypeAtIndex?(tableView: UITableView, idx: number): string;

}

const {ccclass, property, inspector} = cc._decorator

@ccclass

export default class UITableView {

    private node: cc.Node;

    private content: cc.Node;

    private scrollView: cc.ScrollView;

    private _cellFreedDic: Dictionary<cc.Node[]>;

    private _cellUsedArr: cc.Node[];

    private _cellPosArr: number[];

    private __contentOffset: number = 0; //content偏移会在不同尺寸下有个初始的偏移量???

    private __curIdx: number = -1; //最左边位置的itemidx

    public direction: Direction;

    public delegate: UITableViewDelegate;

    public dataSorces: UITableDataSources;

    

    constructor() {

        this._cellFreedDic = new Dictionary<cc.Node[]>();

        this._cellUsedArr = [];

        this._cellPosArr = [];

        

    }

    public init(scrollView: cc.ScrollView, content: cc.Node) {

        this.scrollView = scrollView;

        this.node = scrollView.node;

        this.content = content;

        this.delegate = null;

        this.dataSorces = null;

        if(scrollView.horizontal == scrollView.vertical) {

            console.warn("tableView仅支持一个方向的滑动, 已默认为垂直滑动");

            this.direction = Direction.vertical;

            scrollView.horizontal = false;

            scrollView.vertical = true;

        }else {

            this.direction = scrollView.horizontal ? Direction.horizontal : Direction.vertical;

        }

        

        this._initView();

        this._initEvnet();

    }

    destroy() {

        

        this._removeAllFreeCell();

        console.log("desroy");

    }

    //设置content的位置

    private _initView() {

        this.content.parent.width = this.node.width;

        this.content.parent.height = this.node.height;

        let viewWidget = this._getAndAddWidget(this.content.parent);

        viewWidget.top = 0;

        viewWidget.right = 0;

        viewWidget.left = 0;

        viewWidget.bottom = 0;

        viewWidget.updateAlignment();

        

        this.node.getComponent(cc.Widget) && this.node.getComponent(cc.Widget).updateAlignment();

        

        if(this.direction == Direction.horizontal) {

            this.content.setAnchorPoint(0, 0.5);

            this.content.setPosition(-this.content.parent.width / 2, 0);

            this.content.setContentSize(0, this.node.height);

            this.__contentOffset = this.scrollView.getScrollOffset().x;

        }else {

            this.content.setAnchorPoint(0.5, 1);

            this.content.setPosition(0, this.content.parent.height / 2);

            this.content.setContentSize(this.node.width, 0);

            this.__contentOffset = this.scrollView.getScrollOffset().y;

        }

        if(this.content.getComponent(cc.Layout)) {

            this.content.removeComponent(cc.Layout);

        }

        

    }

    private _getAndAddWidget(node: cc.Node): cc.Widget {

        if(!node.getComponent(cc.Widget)) {

            node.addComponent(cc.Widget);

        }

        return node.getComponent(cc.Widget);

    }

    private _initEvnet() {

        this.node.on("scrolling", () => {

            this.scrollViewDidScroll();

        }, this);

    }

    //计算所有cell的位置

    private _initPosArr() {

        let cellNums: number = this.dataSorces.numberOfRowInTable(this);

        this._cellPosArr = [];

        for (let i = 0; i < cellNums; i++) {

            let cellHeight: number = this.dataSorces.tableCellHeightAtIndex(this, i);

            if(this.direction == Direction.horizontal) {

                if(i == 0) {

                    this._cellPosArr[0] = cellHeight / 2;

                }else {

                    this._cellPosArr[i] = this._cellPosArr[i - 1] + cellHeight;

                }

            }else {

                if(i == 0) {

                    this._cellPosArr[0] = -cellHeight / 2;

                }else {

                    this._cellPosArr[i] = this._cellPosArr[i - 1] - cellHeight;

                }

            }

            

        }

    }

    /**

     * @description: 更新tableView的高度

     */    

    private _updateContentSize() {

        if(!this.dataSorces) return;

        if(this._cellPosArr.length <= 0) return;

        let maxPosIndex: number = this._cellPosArr.length - 1;

        let maxPos = this._cellPosArr[maxPosIndex];

        let maxCellHeight: number = this.dataSorces.tableCellHeightAtIndex(this, maxPosIndex);

        if(this.direction == Direction.horizontal) {

            this.content.setContentSize(maxPos + maxCellHeight / 2, this.node.height);

        }else {

            this.content.setContentSize(this.node.width, -maxPos + maxCellHeight / 2);

        }

        

    }

    /**

     * @description: 更新tableViewCell位置

     */   

    private _updateCellPosition() {

        for (let i = 0; i < this._cellUsedArr.length; i++) {

            const node = this._cellUsedArr[i];

            if(node.getComponent(UITableViewCell)) {

                let idx = node.getComponent(UITableViewCell).getIndx();

                if(this.direction == Direction.horizontal) {

                    node.setPosition(cc.v2(this._cellPosArr[idx], 0));

                }else {

                    node.setPosition(cc.v2(0, this._cellPosArr[idx]));

                }

            } 

        }

    }

    //计算content内可以容纳多少个item(只支持同一个高度的item)

    private getContentMaxNum() {

        if(this.direction == Direction.horizontal) {

            return Math.round(this.node.width / this.dataSorces.tableCellHeightAtIndex(this, 0))

        }else {

            return Math.round(this.node.height / this.dataSorces.tableCellHeightAtIndex(this, 0))

        }

    }

    private scrollViewDidScroll() {

        // console.log(this.scrollView.getScrollOffset().x,  this.__contentOffset);

        let offSet: number = 0;

        if(this.direction == Direction.horizontal) {

            offSet = this.scrollView.getScrollOffset().x - this.__contentOffset;

        }else {

            offSet = this.scrollView.getScrollOffset().y - this.__contentOffset;

        }

        offSet = Math.abs(offSet);

        let view: cc.Node = this.content.parent;

        let tHigh: number = 0;

        let idx: number = 0;

        for (let i = 0; i < this.getCellNums(); i++) {

            tHigh += this.dataSorces.tableCellHeightAtIndex(this, i);

            if(tHigh >= offSet) {

                idx = i;

                break;

            }

        }

        this.delegate && this.delegate.scrollViewDidScroll && this.delegate.scrollViewDidScroll(this, offSet);

        if(this.__curIdx == idx) { //没有新的item生成或销毁避免不必要的计算

            return;

        }

        this.__curIdx = idx;

        let startIndex: number = idx - this.getContentMaxNum();

        let endIndex: number = idx;

        if(startIndex < 0) {

            startIndex = 0;

            endIndex = startIndex + this.getContentMaxNum();

        } 

        let suffHigh: number = 0;

        while (suffHigh <= view.width) {

            if(endIndex >= this.getCellNums() - 1) break;

            endIndex = endIndex + 1;

            suffHigh += this.dataSorces.tableCellHeightAtIndex(this, endIndex);

        }

        endIndex += 1; //往后多生成了一个节点(某些手机iPhone5尺寸的  导致滑动问题)

        for (let i = startIndex; i <= endIndex; i++) {

            this._updateCellIndex(i);      

        }

        this._updateCellPosition();

        // this._reCoverCell(startIndex, endIndex);

    }

    private _reCoverCell(startIndex: number, endIndex: number) {

        for (let i = 0; i < this._cellUsedArr.length; i++) {

            const cell: cc.Node = this._cellUsedArr[i];

            if(cell.getComponent(UITableViewCell)) {

                let idx: number = cell.getComponent(UITableViewCell).getIndx();

                let type: string = cell.getComponent(UITableViewCell).getType();

                if(idx < startIndex || idx > endIndex) {

                    cell.getComponent(UITableViewCell).reset();

                    cell.parent = null;

                    this._cellUsedArr.splice(i, 1);

                    i--;

                    if(!this._cellFreedDic.get(type)) {

                        this._cellFreedDic.put(type, []);

                    }

                    this._cellFreedDic.get(type).push(cell);

                }

            }else {

                console.warn("cell have not UITableViewCell component");

            }

        }

    }

    private _removeAllFreeCell() {

        let keys = this._cellFreedDic.keys;

        for (let i = 0; i < keys.length; i++) {

            const key = keys[i];

            let nodeArr: cc.Node[] = this._cellFreedDic.get(key);

            for (let j = 0; j < nodeArr.length; j++) {

                let node = nodeArr[j];

                if(node && node.isValid) {

                    node.destroy();

                }

            }

            this._cellFreedDic.remove(key);

        }

        this._cellFreedDic = null;

    }

    private _updateCellIndex(idx: number) {

        if(!this.dataSorces) return;

        let cellNums: number = this.dataSorces.numberOfRowInTable(this);

        if(idx > cellNums - 1 || cellNums <= 0) return;

        let cell: cc.Node = this.cellAtIndex(idx);

        if(!cell) {

            cell = this.dataSorces.tableCellAtIndex(this, idx);

            if(!cell) return;

            this._addCell(cell, idx);

        }

        cell.getComponent(UITableViewCell) && cell.getComponent(UITableViewCell).setIndex(idx);

        cell.getComponent(UITableViewCell) && cell.getComponent(UITableViewCell).setType(this.dataSorces.tableCellTypeAtIndex(this, idx));

    }

    private _addCell(cell: cc.Node, zIndex: number) {

        this.content.addChild(cell, zIndex);

        this._cellUsedArr.push(cell);

        cell.on(cc.Node.EventType.TOUCH_END, () => {

            if(cell.getComponent(UITableViewCell)) {

                this.delegate && this.delegate.didSelectRowAtIndex(this, cell.getComponent(UITableViewCell).getIndx());

            }

        })

    }

    public cellAtIndex(idx: number) {

        for (let i = 0; i < this._cellUsedArr.length; i++) {

            const cell: cc.Node = this._cellUsedArr[i];

            if(cell.getComponent(UITableViewCell) && cell.getComponent(UITableViewCell).getIndx() == idx) {

                return cell;

            }

        }

        return null;

    }

    private getCellNums() {

        if(this.dataSorces) {

            return this.dataSorces.numberOfRowInTable(this);

        }

        return 0;

    }

    public reloadData() {

        this._initPosArr();

        this._updateContentSize();

        this.scrollToOffHeight(0);

        this.scrollViewDidScroll();

    }

    public scrollToOffHeight(height: number, timeInSecond: number = 0) {

        let offSet: cc.Vec2 = cc.Vec2.ZERO;

        if(this.direction == Direction.horizontal) {

            offSet.x = height;

        }else {

            offSet.y = height;

        }

        this.node.getComponent(cc.ScrollView).scrollToOffset(offSet, timeInSecond);

        if(timeInSecond == 0) {

            this.scrollViewDidScroll();

        }

    }

    //重复使用cell

    public dequenCell(type: string = "default"): cc.Node {

        let cell: cc.Node = null;

        

        if(this._cellFreedDic.get(type) && this._cellFreedDic.get(type).length > 0) {

            cell = this._cellFreedDic.get(type).shift();

        }

        return cell;

    }

    

}
1赞

使用方式和iOS的UITableView类似 仅支持上下或左右滑动
重复使用cell