/*
* @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