1534 lines
62 KiB
Plaintext
1534 lines
62 KiB
Plaintext
|
||
import { _decorator, ScrollView, Node, Prefab, Enum, Component, EventHandler, Layout, NodePool, Size, instantiate, size, Widget, v3, UITransform, v2, tween } from "cc";
|
||
import li_Component from "./li_Component";
|
||
import ScrollviewListItem from "./ScrollviewListItem";
|
||
import Utils from "./Utils";
|
||
|
||
const { ccclass, property, disallowMultiple, menu, executionOrder, requireComponent } = _decorator;
|
||
|
||
enum TemplateType {
|
||
NODE = 1,
|
||
PREFAB = 2,
|
||
}
|
||
|
||
//滑动列表组件
|
||
@ccclass
|
||
@disallowMultiple()
|
||
@menu('自定义控件/ScrollViewList')
|
||
@requireComponent(ScrollView)
|
||
//脚本生命周期回调的执行优先级。小于 0 的脚本将优先执行,大于 0 的脚本将最后执行。该优先级只对 onLoad, onEnable, start, update 和 lateUpdate 有效,对 onDisable 和 onDestroy 无效。
|
||
@executionOrder(-4000)
|
||
export default class ScrollViewList extends li_Component {
|
||
//模板类型
|
||
@property({
|
||
type: Enum(TemplateType),
|
||
tooltip: '模板类型 Node或者Prefab',
|
||
})
|
||
private templateType: TemplateType = TemplateType.NODE;
|
||
//模板Item(Node)
|
||
@property({
|
||
type: Node,
|
||
tooltip: '模板Item',
|
||
visible() { return this.templateType == TemplateType.NODE; }
|
||
})
|
||
tmpNode: Node = null;
|
||
//模板Item(Prefab)
|
||
@property({
|
||
type: Prefab,
|
||
tooltip: '模板Item',
|
||
visible() { return this.templateType == TemplateType.PREFAB; }
|
||
})
|
||
tmpPrefab: Prefab = null;
|
||
|
||
//渲染事件(渲染器)
|
||
@property({
|
||
type: EventHandler,
|
||
tooltip: '渲染事件(渲染器)',
|
||
})
|
||
private renderEvent: EventHandler = new EventHandler();
|
||
//滚动结束触发事件
|
||
@property({
|
||
type: EventHandler,
|
||
tooltip: '滚动结束时触发事件',
|
||
})
|
||
private renderEventUpData: EventHandler = new EventHandler();
|
||
|
||
//当前选择id
|
||
private _virtual: boolean = true;//是否为虚拟列表(动态列表)
|
||
private _selectedId: number = -1;
|
||
private _lastSelectedId: number;
|
||
private multSelected: number[];
|
||
private _forceUpdate: boolean = false;
|
||
private _align: number;
|
||
private _horizontalDir: number;
|
||
private _verticalDir: number;
|
||
private _startAxis: number;
|
||
private _alignCalcType: number;
|
||
public content: Node;
|
||
private contentUITF: UITransform;
|
||
private firstListId: number;
|
||
public displayItemNum: number;
|
||
private _updateDone: boolean = true;
|
||
private _updateCounter: number;
|
||
public _actualNumItems: number;
|
||
private _cyclicNum: number;
|
||
private _cyclicPos1: number;
|
||
private _cyclicPos2: number;
|
||
//列表数量
|
||
@property({
|
||
serializable: false
|
||
})
|
||
private _numItems: number = 0;
|
||
set numItems(val: number) {
|
||
if (!this.checkInited(false))
|
||
return;
|
||
if (val == null || val < 0) {
|
||
//hg_utils.hgLog('numItems set the wrong::', val);
|
||
return;
|
||
}
|
||
this._actualNumItems = this._numItems = val;
|
||
this._forceUpdate = true;
|
||
|
||
if (this._virtual) {
|
||
this._resizeContent();
|
||
this._onScrolling();
|
||
} else {
|
||
let layout: Layout = this.content.getComponent(Layout);
|
||
if (layout) {
|
||
layout.enabled = true;
|
||
}
|
||
this._delRedundantItem();
|
||
|
||
this.firstListId = 0;
|
||
for (let n: number = 0; n < val; n++) {
|
||
this._createOrUpdateItem2(n);
|
||
}
|
||
this.displayItemNum = val;
|
||
}
|
||
}
|
||
get numItems() {
|
||
return this._actualNumItems;
|
||
}
|
||
|
||
private _inited: boolean = false;
|
||
private _scrollView: ScrollView;
|
||
get scrollView() {
|
||
return this._scrollView;
|
||
}
|
||
private _uitf: UITransform;
|
||
private _layout: Layout;
|
||
private _resizeMode//: Layout.ResizeMode;
|
||
private _topGap: number;
|
||
private _rightGap: number;
|
||
private _bottomGap: number;
|
||
private _leftGap: number;
|
||
|
||
private _columnGap: number;
|
||
private _lineGap: number;
|
||
private _colLineNum: number;
|
||
|
||
private _lastDisplayData: number[];
|
||
public displayData: any[];
|
||
private _pool: NodePool;
|
||
|
||
private _itemTmp: Node;
|
||
private _needUpdateWidget: boolean = false;
|
||
private _itemSize: Size;
|
||
private _sizeType: boolean;
|
||
|
||
public _customSize: any;
|
||
|
||
private frameCount: number;
|
||
private _aniDelRuning: boolean = false;
|
||
private viewTop: number;
|
||
private viewRight: number;
|
||
private viewBottom: number;
|
||
private viewLeft: number;
|
||
|
||
private _doneAfterUpdate: boolean = false;
|
||
|
||
private elasticTop: number;
|
||
private elasticRight: number;
|
||
private elasticBottom: number;
|
||
private elasticLeft: number;
|
||
|
||
private scrollToListId: number;
|
||
|
||
private adhering: boolean = false;
|
||
|
||
private _adheringBarrier: boolean = false;
|
||
private nearestListId: number;
|
||
|
||
public curPageNum: number = 0;
|
||
private _beganPos: number;
|
||
private _scrollPos: number;
|
||
|
||
private _scrollToListId: number;
|
||
private _scrollToEndTime: number;
|
||
private _scrollToSo: any;
|
||
|
||
private _lack: boolean;
|
||
private _allItemSize: number;
|
||
private _allItemSizeNoEdge: number;
|
||
|
||
private _scrollItem: any;//当前控制 ScrollView 滚动的 Item
|
||
|
||
|
||
set TopGap(val: number) {
|
||
this._topGap = val
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
onLoad() {
|
||
this._init();
|
||
}
|
||
|
||
onNodeDestroy() {
|
||
if (this._itemTmp && this._itemTmp.isValid)
|
||
this._itemTmp.destroy();
|
||
if (this.tmpNode && this.tmpNode.isValid)
|
||
this.tmpNode.destroy();
|
||
while (this._pool && this._pool.size()) {
|
||
let node = this._pool.get();
|
||
node.destroy();
|
||
}
|
||
}
|
||
|
||
onEnable() {
|
||
this._registerEvent();
|
||
this._init();
|
||
}
|
||
|
||
onDisable() {
|
||
this._unregisterEvent();
|
||
}
|
||
//注册事件
|
||
_registerEvent() {
|
||
this.node.on("touch-start", this._onTouchStart, this);
|
||
this.node.on("touch-end", this._onTouchUp, this);
|
||
this.node.on("touch-cancel", this._onTouchCancelled, this);
|
||
this.node.on("scroll-began", this._onScrollBegan, this);
|
||
this.node.on("scroll-ended", this._onScrollEnded, this);
|
||
this.node.on("scrolling", this._onScrolling, this);
|
||
this.node.on("size-changed", this._onSizeChanged, this);
|
||
}
|
||
//卸载事件
|
||
_unregisterEvent() {
|
||
this.node.off("touch-start", this._onTouchStart, this);
|
||
this.node.off("touch-end", this._onTouchUp, this);
|
||
this.node.off("touch-cancel", this._onTouchCancelled, this);
|
||
this.node.off("scroll-began", this._onScrollBegan, this);
|
||
this.node.off("scroll-ended", this._onScrollEnded, this);
|
||
this.node.off("scrolling", this._onScrolling, this);
|
||
this.node.off("size-changed", this._onSizeChanged, this);
|
||
}
|
||
//初始化各种..
|
||
_init() {
|
||
if (this._inited)
|
||
return;
|
||
if (this.tmpPrefab == null && this.tmpNode == null)
|
||
return;
|
||
|
||
this._scrollView = this.node.getComponent(ScrollView);
|
||
this.content = this._scrollView.content;
|
||
if (!this.content) {
|
||
//hg_utils.hgLog(this.node.name + "'s ScrollView unset content!");
|
||
return;
|
||
}
|
||
|
||
this.contentUITF = this.content.getComponent(UITransform);
|
||
this._uitf = this.node.getComponent(UITransform);
|
||
this._layout = this.content.getComponent(Layout);
|
||
|
||
this._align = this._layout.type; //排列模式
|
||
this._resizeMode = this._layout.resizeMode; //自适应模式
|
||
this._startAxis = this._layout.startAxis;
|
||
|
||
this._topGap = this._layout.paddingTop; //顶边距
|
||
this._rightGap = this._layout.paddingRight; //右边距
|
||
this._bottomGap = this._layout.paddingBottom; //底边距
|
||
this._leftGap = this._layout.paddingLeft; //左边距
|
||
|
||
this._columnGap = this._layout.spacingX; //列距
|
||
this._lineGap = this._layout.spacingY; //行距
|
||
|
||
this._colLineNum; //列数或行数(非GRID模式则=1,表示单列或单行);
|
||
|
||
this._verticalDir = this._layout.verticalDirection; //垂直排列子节点的方向
|
||
this._horizontalDir = this._layout.horizontalDirection; //水平排列子节点的方向
|
||
|
||
this.setTemplateItem(instantiate(this.templateType == TemplateType.PREFAB ? this.tmpPrefab : this.tmpNode));
|
||
|
||
this._lastDisplayData = []; //最后一次刷新的数据
|
||
this.displayData = []; //当前数据
|
||
while (this._pool && this._pool.size()) {
|
||
let node = this._pool.get();
|
||
node.destroy();
|
||
}
|
||
this._pool = new NodePool(); //这是个池子..
|
||
this._forceUpdate = false; //是否强制更新
|
||
this._updateCounter = 0; //当前分帧渲染帧数
|
||
this._updateDone = true; //分帧渲染是否完成
|
||
|
||
this.curPageNum = 0; //当前页数
|
||
|
||
switch (this._align) {
|
||
case Layout.Type.HORIZONTAL: {
|
||
switch (this._horizontalDir) {
|
||
case Layout.HorizontalDirection.LEFT_TO_RIGHT:
|
||
this._alignCalcType = 1;
|
||
break;
|
||
case Layout.HorizontalDirection.RIGHT_TO_LEFT:
|
||
this._alignCalcType = 2;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
case Layout.Type.VERTICAL: {
|
||
switch (this._verticalDir) {
|
||
case Layout.VerticalDirection.TOP_TO_BOTTOM:
|
||
this._alignCalcType = 3;
|
||
break;
|
||
case Layout.VerticalDirection.BOTTOM_TO_TOP:
|
||
this._alignCalcType = 4;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
case Layout.Type.GRID: {
|
||
switch (this._startAxis) {
|
||
case Layout.AxisDirection.HORIZONTAL:
|
||
switch (this._verticalDir) {
|
||
case Layout.VerticalDirection.TOP_TO_BOTTOM:
|
||
this._alignCalcType = 3;
|
||
break;
|
||
case Layout.VerticalDirection.BOTTOM_TO_TOP:
|
||
this._alignCalcType = 4;
|
||
break;
|
||
}
|
||
break;
|
||
case Layout.AxisDirection.VERTICAL:
|
||
switch (this._horizontalDir) {
|
||
case Layout.HorizontalDirection.LEFT_TO_RIGHT:
|
||
this._alignCalcType = 1;
|
||
break;
|
||
case Layout.HorizontalDirection.RIGHT_TO_LEFT:
|
||
this._alignCalcType = 2;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
this.content.removeAllChildren();
|
||
this._inited = true;
|
||
}
|
||
/**
|
||
* 为了实现循环列表,必须覆写ScrollView的某些函数
|
||
* @param {Number} dt
|
||
*/
|
||
_processAutoScrolling(dt: number) {
|
||
let brakingFactor: number = 1;
|
||
this._scrollView['_autoScrollAccumulatedTime'] += dt * (1 / brakingFactor);
|
||
|
||
let percentage: number = Math.min(1, this._scrollView['_autoScrollAccumulatedTime'] / this._scrollView['_autoScrollTotalTime']);
|
||
if (this._scrollView['_autoScrollAttenuate']) {
|
||
let time: number = percentage - 1;
|
||
percentage = time * time * time * time * time + 1;
|
||
}
|
||
|
||
let astdelta = this._scrollView['_autoScrollTargetDelta']//.mul(percentage)
|
||
let newDeltav3 = v3(astdelta.x*percentage, astdelta.y*percentage, astdelta.z*percentage)
|
||
let newPosition: any = this._scrollView['_autoScrollStartPosition'].add(newDeltav3);
|
||
let EPSILON: number = this._scrollView['getScrollEndedEventTiming']();
|
||
let reachedEnd: boolean = Math.abs(percentage - 1) <= EPSILON;
|
||
|
||
let fireEvent: boolean = Math.abs(percentage - 1) <= this._scrollView['getScrollEndedEventTiming']();
|
||
if (fireEvent && !this._scrollView['_isScrollEndedWithThresholdEventFired']) {
|
||
this._scrollView['_dispatchEvent']('scroll-ended-with-threshold');
|
||
this._scrollView['_isScrollEndedWithThresholdEventFired'] = true;
|
||
}
|
||
|
||
if (reachedEnd) {
|
||
this._scrollView['_autoScrolling'] = false;
|
||
}
|
||
|
||
let deltaMove: any = newPosition.sub(this._scrollView.content.getPosition()); //断点
|
||
// this._scrollView['_moveContent'](this._scrollView['_clampDelta'](deltaMove), reachedEnd);
|
||
this._scrollView['_dispatchEvent'](ScrollView.EventType.SCROLLING);
|
||
|
||
if (!this._scrollView['_autoScrolling']) {
|
||
this._scrollView['_isBouncing'] = false;
|
||
this._scrollView['_scrolling'] = false;
|
||
this._scrollView['_dispatchEvent'](ScrollView.EventType.SCROLL_ENDED);
|
||
}
|
||
}
|
||
//设置模板Item
|
||
setTemplateItem(item: any) {
|
||
if (!item)
|
||
return;
|
||
if (this.tmpNode == null && this.tmpPrefab == null) {
|
||
if (item instanceof Prefab) {
|
||
this.templateType = TemplateType.PREFAB;
|
||
this.tmpPrefab = item;
|
||
} else if (item instanceof Node) {
|
||
this.templateType = TemplateType.NODE;
|
||
this.tmpNode = item;
|
||
}
|
||
this._init();
|
||
return;
|
||
}
|
||
this._itemTmp = item;
|
||
|
||
if (this._resizeMode == Layout.ResizeMode.CHILDREN){
|
||
this._itemSize = this._layout.cellSize;
|
||
}
|
||
else{
|
||
let uitf = item.getComponent(UITransform)
|
||
this._itemSize = size(uitf.width, uitf.height);
|
||
}
|
||
|
||
//获取ListItem,如果没有就取消选择模式
|
||
let com = item.getComponent(ScrollviewListItem);
|
||
let remove = false;
|
||
if (!com)
|
||
remove = true;
|
||
|
||
com = item.getComponent(Widget);
|
||
if (com && com.enabled) {
|
||
this._needUpdateWidget = true;
|
||
}
|
||
|
||
switch (this._align) {
|
||
case Layout.Type.HORIZONTAL:
|
||
this._colLineNum = 1;
|
||
this._sizeType = false;
|
||
break;
|
||
case Layout.Type.VERTICAL:
|
||
this._colLineNum = 1;
|
||
this._sizeType = true;
|
||
break;
|
||
case Layout.Type.GRID:
|
||
switch (this._startAxis) {
|
||
case Layout.AxisDirection.HORIZONTAL:
|
||
//计算列数
|
||
let trimW: number = this.contentUITF.width - this._leftGap - this._rightGap;
|
||
this._colLineNum = Math.floor((trimW + this._columnGap) / (this._itemSize.width + this._columnGap));
|
||
this._sizeType = true;
|
||
break;
|
||
case Layout.AxisDirection.VERTICAL:
|
||
//计算行数
|
||
let trimH: number = this.contentUITF.height - this._topGap - this._bottomGap;
|
||
this._colLineNum = Math.floor((trimH + this._lineGap) / (this._itemSize.height + this._lineGap));
|
||
this._sizeType = false;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
/**
|
||
* 检查是否初始化
|
||
* @param {Boolean} printLog 是否打印错误信息
|
||
* @returns
|
||
*/
|
||
checkInited(printLog: boolean = true) {
|
||
if (!this._inited) {
|
||
if (printLog)
|
||
//hg_utils.hgLog('List initialization not completed!');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
//禁用 Layout 组件,自行计算 Content Size
|
||
_resizeContent() {
|
||
let result: number;
|
||
|
||
switch (this._align) {
|
||
case Layout.Type.HORIZONTAL: {
|
||
if (this._customSize) {
|
||
let fixed: any = this._getFixedSize(null);
|
||
result = this._leftGap + fixed.val + (this._itemSize.width * (this._numItems - fixed.count)) + (this._columnGap * (this._numItems - 1)) + this._rightGap;
|
||
} else {
|
||
result = this._leftGap + (this._itemSize.width * this._numItems) + (this._columnGap * (this._numItems - 1)) + this._rightGap;
|
||
}
|
||
break;
|
||
}
|
||
case Layout.Type.VERTICAL: {
|
||
if (this._customSize) {
|
||
let fixed: any = this._getFixedSize(null);
|
||
result = this._topGap + fixed.val + (this._itemSize.height * (this._numItems - fixed.count)) + (this._lineGap * (this._numItems - 1)) + this._bottomGap;
|
||
} else {
|
||
result = this._topGap + (this._itemSize.height * this._numItems) + (this._lineGap * (this._numItems - 1)) + this._bottomGap;
|
||
}
|
||
break;
|
||
}
|
||
case Layout.Type.GRID: {
|
||
switch (this._startAxis) {
|
||
case Layout.AxisDirection.HORIZONTAL:
|
||
let lineNum: number = Math.ceil(this._numItems / this._colLineNum);
|
||
result = this._topGap + (this._itemSize.height * lineNum) + (this._lineGap * (lineNum - 1)) + this._bottomGap;
|
||
break;
|
||
case Layout.AxisDirection.VERTICAL:
|
||
let colNum: number = Math.ceil(this._numItems / this._colLineNum);
|
||
result = this._leftGap + (this._itemSize.width * colNum) + (this._columnGap * (colNum - 1)) + this._rightGap;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if (!this.content) {
|
||
return;
|
||
}
|
||
let layout: Layout = this.content.getComponent(Layout);
|
||
if (layout)
|
||
layout.enabled = false;
|
||
|
||
this._allItemSize = result;
|
||
this._allItemSizeNoEdge = this._allItemSize - (this._sizeType ? (this._topGap + this._bottomGap) : (this._leftGap + this._rightGap));
|
||
|
||
this._lack = this._allItemSize < (this._sizeType ? this._uitf.height : this._uitf.width);
|
||
let slideOffset: number = .1;
|
||
|
||
let targetWH: number = this._lack ? ((this._sizeType ? this._uitf.height : this._uitf.width) - slideOffset) : (this._allItemSize);
|
||
if (targetWH < 0)
|
||
targetWH = 0;
|
||
|
||
if (this._sizeType) {
|
||
this.contentUITF.height = targetWH;
|
||
} else {
|
||
this.contentUITF.width = targetWH;
|
||
}
|
||
}
|
||
|
||
|
||
//计算可视范围
|
||
_calcViewPos() {
|
||
if (!this.content) return;
|
||
let scrollPos: any = this.content.getPosition();
|
||
switch (this._alignCalcType) {
|
||
case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
|
||
this.elasticLeft = scrollPos.x > 0 ? scrollPos.x : 0;
|
||
this.viewLeft = (scrollPos.x < 0 ? -scrollPos.x : 0) - this.elasticLeft;
|
||
this.viewRight = this.viewLeft + this._uitf.width;
|
||
this.elasticRight = this.viewRight > this.contentUITF.width ? Math.abs(this.viewRight - this.contentUITF.width) : 0;
|
||
this.viewRight += this.elasticRight;
|
||
// //hg_utils.hgLog(this.elasticLeft, this.elasticRight, this.viewLeft, this.viewRight);
|
||
break;
|
||
case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
|
||
this.elasticRight = scrollPos.x < 0 ? -scrollPos.x : 0;
|
||
this.viewRight = (scrollPos.x > 0 ? -scrollPos.x : 0) + this.elasticRight;
|
||
this.viewLeft = this.viewRight - this._uitf.width;
|
||
this.elasticLeft = this.viewLeft < -this.contentUITF.width ? Math.abs(this.viewLeft + this.contentUITF.width) : 0;
|
||
this.viewLeft -= this.elasticLeft;
|
||
// //hg_utils.hgLog(this.elasticLeft, this.elasticRight, this.viewLeft, this.viewRight);
|
||
break;
|
||
case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
|
||
this.elasticTop = scrollPos.y < 0 ? Math.abs(scrollPos.y) : 0;
|
||
this.viewTop = (scrollPos.y > 0 ? -scrollPos.y : 0) + this.elasticTop;
|
||
this.viewBottom = this.viewTop - this._uitf.height;
|
||
this.elasticBottom = this.viewBottom < -this.contentUITF.height ? Math.abs(this.viewBottom + this.contentUITF.height) : 0;
|
||
this.viewBottom += this.elasticBottom;
|
||
// //hg_utils.hgLog(this.elasticTop, this.elasticBottom, this.viewTop, this.viewBottom);
|
||
break;
|
||
case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
|
||
this.elasticBottom = scrollPos.y > 0 ? Math.abs(scrollPos.y) : 0;
|
||
this.viewBottom = (scrollPos.y < 0 ? -scrollPos.y : 0) - this.elasticBottom;
|
||
this.viewTop = this.viewBottom + this._uitf.height;
|
||
this.elasticTop = this.viewTop > this.contentUITF.height ? Math.abs(this.viewTop - this.contentUITF.height) : 0;
|
||
this.viewTop -= this.elasticTop;
|
||
// //hg_utils.hgLog(this.elasticTop, this.elasticBottom, this.viewTop, this.viewBottom);
|
||
break;
|
||
}
|
||
}
|
||
//计算位置 根据id
|
||
_calcItemPos(id: number) {
|
||
let width: number, height: number, top: number, bottom: number, left: number, right: number, itemX: number, itemY: number;
|
||
if (!this._itemSize) return {};
|
||
switch (this._align) {
|
||
case Layout.Type.HORIZONTAL:
|
||
switch (this._horizontalDir) {
|
||
case Layout.HorizontalDirection.LEFT_TO_RIGHT: {
|
||
if (this._customSize) {
|
||
let fixed: any = this._getFixedSize(id);
|
||
left = this._leftGap + ((this._itemSize.width + this._columnGap) * (id - fixed.count)) + (fixed.val + (this._columnGap * fixed.count));
|
||
let cs: number = this._customSize[id];
|
||
width = (cs > 0 ? cs : this._itemSize.width);
|
||
} else {
|
||
left = this._leftGap + ((this._itemSize.width + this._columnGap) * id);
|
||
width = this._itemSize.width;
|
||
}
|
||
right = left + width;
|
||
return {
|
||
id: id,
|
||
left: left,
|
||
right: right,
|
||
x: left + (this._itemTmp.getComponent(UITransform).anchorX * width),
|
||
y: this._itemTmp.position.y,
|
||
};
|
||
}
|
||
case Layout.HorizontalDirection.RIGHT_TO_LEFT: {
|
||
if (this._customSize) {
|
||
let fixed: any = this._getFixedSize(id);
|
||
right = -this._rightGap - ((this._itemSize.width + this._columnGap) * (id - fixed.count)) - (fixed.val + (this._columnGap * fixed.count));
|
||
let cs: number = this._customSize[id];
|
||
width = (cs > 0 ? cs : this._itemSize.width);
|
||
} else {
|
||
right = -this._rightGap - ((this._itemSize.width + this._columnGap) * id);
|
||
width = this._itemSize.width;
|
||
}
|
||
left = right - width;
|
||
return {
|
||
id: id,
|
||
right: right,
|
||
left: left,
|
||
x: left + (this._itemTmp.getComponent(UITransform).anchorX * width),
|
||
y: this._itemTmp.position.y,
|
||
};
|
||
}
|
||
}
|
||
break;
|
||
case Layout.Type.VERTICAL: {
|
||
switch (this._verticalDir) {
|
||
case Layout.VerticalDirection.TOP_TO_BOTTOM: {
|
||
if (this._customSize) {
|
||
let fixed: any = this._getFixedSize(id);
|
||
top = -this._topGap - ((this._itemSize.height + this._lineGap) * (id - fixed.count)) - (fixed.val + (this._lineGap * fixed.count));
|
||
let cs: number = this._customSize[id];
|
||
height = (cs > 0 ? cs : this._itemSize.height);
|
||
} else {
|
||
top = -this._topGap - ((this._itemSize.height + this._lineGap) * id);
|
||
height = this._itemSize.height;
|
||
}
|
||
bottom = top - height;
|
||
return {
|
||
id: id,
|
||
top: top,
|
||
bottom: bottom,
|
||
x: this._itemTmp.position.x,
|
||
y: bottom + (this._itemTmp.getComponent(UITransform).anchorY * height),
|
||
};
|
||
}
|
||
case Layout.VerticalDirection.BOTTOM_TO_TOP: {
|
||
if (this._customSize) {
|
||
let fixed: any = this._getFixedSize(id);
|
||
bottom = this._bottomGap + ((this._itemSize.height + this._lineGap) * (id - fixed.count)) + (fixed.val + (this._lineGap * fixed.count));
|
||
let cs: number = this._customSize[id];
|
||
height = (cs > 0 ? cs : this._itemSize.height);
|
||
} else {
|
||
bottom = this._bottomGap + ((this._itemSize.height + this._lineGap) * id);
|
||
height = this._itemSize.height;
|
||
}
|
||
top = bottom + height;
|
||
return {
|
||
id: id,
|
||
top: top,
|
||
bottom: bottom,
|
||
x: this._itemTmp.position.x,
|
||
y: bottom + (this._itemTmp.getComponent(UITransform).anchorY * height),
|
||
};
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
case Layout.Type.GRID: {
|
||
let colLine: number = Math.floor(id / this._colLineNum);
|
||
switch (this._startAxis) {
|
||
case Layout.AxisDirection.HORIZONTAL: {
|
||
switch (this._verticalDir) {
|
||
case Layout.VerticalDirection.TOP_TO_BOTTOM: {
|
||
top = -this._topGap - ((this._itemSize.height + this._lineGap) * colLine);
|
||
bottom = top - this._itemSize.height;
|
||
itemY = bottom + (this._itemTmp.getComponent(UITransform).anchorY * this._itemSize.height);
|
||
break;
|
||
}
|
||
case Layout.VerticalDirection.BOTTOM_TO_TOP: {
|
||
bottom = this._bottomGap + ((this._itemSize.height + this._lineGap) * colLine);
|
||
top = bottom + this._itemSize.height;
|
||
itemY = bottom + (this._itemTmp.getComponent(UITransform).anchorY * this._itemSize.height);
|
||
break;
|
||
}
|
||
}
|
||
itemX = this._leftGap + ((id % this._colLineNum) * (this._itemSize.width + this._columnGap));
|
||
switch (this._horizontalDir) {
|
||
case Layout.HorizontalDirection.LEFT_TO_RIGHT: {
|
||
itemX += (this._itemTmp.getComponent(UITransform).anchorX * this._itemSize.width);
|
||
itemX -= (this.contentUITF.anchorX * this.contentUITF.width);
|
||
break;
|
||
}
|
||
case Layout.HorizontalDirection.RIGHT_TO_LEFT: {
|
||
itemX += ((1 - this._itemTmp.getComponent(UITransform).anchorX) * this._itemSize.width);
|
||
itemX -= ((1 - this.contentUITF.anchorX) * this.contentUITF.width);
|
||
itemX *= -1;
|
||
break;
|
||
}
|
||
}
|
||
return {
|
||
id: id,
|
||
top: top,
|
||
bottom: bottom,
|
||
x: itemX,
|
||
y: itemY,
|
||
};
|
||
}
|
||
case Layout.AxisDirection.VERTICAL: {
|
||
switch (this._horizontalDir) {
|
||
case Layout.HorizontalDirection.LEFT_TO_RIGHT: {
|
||
left = this._leftGap + ((this._itemSize.width + this._columnGap) * colLine);
|
||
right = left + this._itemSize.width;
|
||
itemX = left + (this._itemTmp.getComponent(UITransform).anchorX * this._itemSize.width);
|
||
itemX -= (this.contentUITF.anchorX * this.contentUITF.width);
|
||
break;
|
||
}
|
||
case Layout.HorizontalDirection.RIGHT_TO_LEFT: {
|
||
right = -this._rightGap - ((this._itemSize.width + this._columnGap) * colLine);
|
||
left = right - this._itemSize.width;
|
||
itemX = left + (this._itemTmp.getComponent(UITransform).anchorX * this._itemSize.width);
|
||
itemX += ((1 - this.contentUITF.anchorX) * this.contentUITF.width);
|
||
break;
|
||
}
|
||
}
|
||
itemY = -this._topGap - ((id % this._colLineNum) * (this._itemSize.height + this._lineGap));
|
||
switch (this._verticalDir) {
|
||
case Layout.VerticalDirection.TOP_TO_BOTTOM: {
|
||
itemY -= ((1 - this._itemTmp.getComponent(UITransform).anchorY) * this._itemSize.height);
|
||
itemY += ((1 - this.contentUITF.anchorY) * this.contentUITF.height);
|
||
break;
|
||
}
|
||
case Layout.VerticalDirection.BOTTOM_TO_TOP: {
|
||
itemY -= ((this._itemTmp.getComponent(UITransform).anchorY) * this._itemSize.height);
|
||
itemY += (this.contentUITF.anchorY * this.contentUITF.height);
|
||
itemY *= -1;
|
||
break;
|
||
}
|
||
}
|
||
return {
|
||
id: id,
|
||
left: left,
|
||
right: right,
|
||
x: itemX,
|
||
y: itemY,
|
||
};
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
//计算已存在的Item的位置
|
||
_calcExistItemPos(id: number) {
|
||
let item: Node = this.getItemByListId(id);
|
||
if (!item)
|
||
return null;
|
||
let data: any = {
|
||
id: id,
|
||
x: item.position.x,
|
||
y: item.position.y,
|
||
};
|
||
let uitf = item.getComponent(UITransform)
|
||
if (this._sizeType) {
|
||
data.top = item.position.y + (uitf.height * (1 - uitf.anchorY));
|
||
data.bottom = item.position.y - (uitf.height * uitf.anchorY);
|
||
} else {
|
||
data.left = item.position.x - (uitf.width * uitf.anchorX);
|
||
data.right = item.position.x + (uitf.width * (1 - uitf.anchorX));
|
||
}
|
||
return data;
|
||
}
|
||
//获取Item位置
|
||
getItemPos(id: number) {
|
||
return this._calcItemPos(id);
|
||
}
|
||
//获取固定尺寸
|
||
_getFixedSize(listId: number) {
|
||
if (!this._customSize)
|
||
return null;
|
||
if (listId == null)
|
||
listId = this._numItems;
|
||
let fixed: number = 0;
|
||
let count: number = 0;
|
||
for (let id in this._customSize) {
|
||
if (parseInt(id) < listId) {
|
||
fixed += this._customSize[id];
|
||
count++;
|
||
}
|
||
}
|
||
return {
|
||
val: fixed,
|
||
count: count,
|
||
};
|
||
}
|
||
//滚动开始时..
|
||
private canRefresh: boolean = false;
|
||
_onScrollBegan() {
|
||
// Utils.Log("滚动容器 _onScrollBegan")
|
||
this.canRefresh = false;
|
||
this._beganPos = this._sizeType ? this.viewTop : this.viewLeft;
|
||
}
|
||
|
||
//滚动进行时...
|
||
_onScrolling(ev: Event = null) {
|
||
// Utils.Log("滚动容器 _onScrolling")
|
||
|
||
if (!this._inited)
|
||
return
|
||
|
||
if (this.frameCount == null)
|
||
this.frameCount = 0;
|
||
if (!this._forceUpdate && (ev && ev.type != ScrollView.EventType.SCROLL_ENDED) && this.frameCount > 0) {
|
||
this.frameCount--;
|
||
return;
|
||
} else{
|
||
this.frameCount = 0;
|
||
}
|
||
|
||
if (this._aniDelRuning)
|
||
return;
|
||
|
||
let scorv = this.node.getComponent(ScrollView)
|
||
let maxPos = scorv.getMaxScrollOffset();
|
||
let curPos = scorv.getScrollOffset();
|
||
if (scorv.vertical) {
|
||
if (curPos.y - maxPos.y > 100) {
|
||
this.canRefresh = true;
|
||
}
|
||
} else if (scorv.horizontal) {
|
||
if (curPos.x - maxPos.x > 100) {
|
||
this.canRefresh = true;
|
||
}
|
||
}
|
||
|
||
this._calcViewPos();
|
||
|
||
let vTop: number, vRight: number, vBottom: number, vLeft: number;
|
||
if (this._sizeType) {
|
||
vTop = this.viewTop;
|
||
vBottom = this.viewBottom;
|
||
} else {
|
||
vRight = this.viewRight;
|
||
vLeft = this.viewLeft;
|
||
}
|
||
|
||
if (this._virtual) {
|
||
this.displayData = [];
|
||
let itemPos: any;
|
||
|
||
let curId: number = 0;
|
||
let endId: number = this._numItems - 1;
|
||
|
||
if (this._customSize) {
|
||
let breakFor: boolean = false;
|
||
//如果该item的位置在可视区域内,就推入displayData
|
||
for (; curId <= endId && !breakFor; curId++) {
|
||
itemPos = this._calcItemPos(curId);
|
||
switch (this._align) {
|
||
case Layout.Type.HORIZONTAL:
|
||
if (itemPos.right >= vLeft && itemPos.left <= vRight) {
|
||
this.displayData.push(itemPos);
|
||
} else if (curId != 0 && this.displayData.length > 0) {
|
||
breakFor = true;
|
||
}
|
||
break;
|
||
case Layout.Type.VERTICAL:
|
||
if (itemPos.bottom <= vTop && itemPos.top >= vBottom) {
|
||
this.displayData.push(itemPos);
|
||
} else if (curId != 0 && this.displayData.length > 0) {
|
||
breakFor = true;
|
||
}
|
||
break;
|
||
case Layout.Type.GRID:
|
||
switch (this._startAxis) {
|
||
case Layout.AxisDirection.HORIZONTAL:
|
||
if (itemPos.bottom <= vTop && itemPos.top >= vBottom) {
|
||
this.displayData.push(itemPos);
|
||
} else if (curId != 0 && this.displayData.length > 0) {
|
||
breakFor = true;
|
||
}
|
||
break;
|
||
case Layout.AxisDirection.VERTICAL:
|
||
if (itemPos.right >= vLeft && itemPos.left <= vRight) {
|
||
this.displayData.push(itemPos);
|
||
} else if (curId != 0 && this.displayData.length > 0) {
|
||
breakFor = true;
|
||
}
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
if (!this._itemSize) {
|
||
return;
|
||
}
|
||
let ww: number = this._itemSize.width + this._columnGap;
|
||
let hh: number = this._itemSize.height + this._lineGap;
|
||
switch (this._alignCalcType) {
|
||
case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
|
||
curId = (vLeft + this._leftGap) / ww;
|
||
endId = (vRight + this._rightGap) / ww;
|
||
break;
|
||
case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
|
||
curId = (-vRight - this._rightGap) / ww;
|
||
endId = (-vLeft - this._leftGap) / ww;
|
||
break;
|
||
case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
|
||
curId = (-vTop - this._topGap) / hh;
|
||
endId = (-vBottom - this._bottomGap) / hh;
|
||
break;
|
||
case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
|
||
curId = (vBottom + this._bottomGap) / hh;
|
||
endId = (vTop + this._topGap) / hh;
|
||
break;
|
||
}
|
||
curId = Math.floor(curId) * this._colLineNum;
|
||
endId = Math.ceil(endId) * this._colLineNum;
|
||
endId--;
|
||
if (curId < 0)
|
||
curId = 0;
|
||
if (endId >= this._numItems)
|
||
endId = this._numItems - 1;
|
||
for (; curId <= endId; curId++) {
|
||
this.displayData.push(this._calcItemPos(curId));
|
||
}
|
||
}
|
||
this._delRedundantItem();
|
||
if (this.displayData.length <= 0 || !this._numItems) { //if none, delete all.
|
||
this._lastDisplayData = [];
|
||
return;
|
||
}
|
||
this.firstListId = this.displayData[0].id;
|
||
this.displayItemNum = this.displayData.length;
|
||
|
||
let len: number = this._lastDisplayData.length;
|
||
|
||
let haveDataChange: boolean = this.displayItemNum != len;
|
||
if (haveDataChange) {
|
||
// 因List的显示数据是有序的,所以只需要判断数组长度是否相等,以及头、尾两个元素是否相等即可。
|
||
haveDataChange = this.firstListId != this._lastDisplayData[0] || this.displayData[this.displayItemNum - 1].id != this._lastDisplayData[len - 1];
|
||
}
|
||
|
||
if (this._forceUpdate || haveDataChange) { //如果是强制更新
|
||
//直接渲染
|
||
this._lastDisplayData = [];
|
||
// //hg_utils.hgLog('List Display Data II::', this.displayData);
|
||
for (let c = 0; c < this.displayItemNum; c++) {
|
||
this._createOrUpdateItem(this.displayData[c]);
|
||
}
|
||
this._forceUpdate = false;
|
||
}
|
||
this._calcNearestItem();
|
||
}
|
||
}
|
||
//滚动结束时..
|
||
_onScrollEnded() {
|
||
// Utils.Log("滚动容器 _onScrollEnded")
|
||
if (this.scrollToListId != null) {
|
||
let item: any = this.getItemByListId(this.scrollToListId);
|
||
this.scrollToListId = null;
|
||
if (item) {
|
||
// item.runAction(cc.sequence(
|
||
// cc.scaleTo(.1, 1.06),
|
||
// cc.scaleTo(.1, 1),
|
||
// ));
|
||
tween(item)
|
||
.to(0.1, {scale: v3(1.06, 1.06, 1.06)})
|
||
.to(0.1, {scale: v3(1, 1, 1)})
|
||
.start()
|
||
}
|
||
}
|
||
this._onScrolling();
|
||
|
||
if (this.canRefresh) {
|
||
this.canRefresh = false;
|
||
Component.EventHandler.emitEvents([this.renderEventUpData]);
|
||
}
|
||
}
|
||
// 触摸时
|
||
_onTouchStart(ev, captureListeners) {
|
||
// Utils.Log("滚动容器 _onTouchStart")
|
||
this.canRefresh = false;
|
||
let isMe = ev.eventPhase === Event.AT_TARGET && ev.target === this.node;
|
||
if (!isMe) {
|
||
let itemNode: any = ev.target;
|
||
while (itemNode._listId == null && itemNode.parent)
|
||
itemNode = itemNode.parent;
|
||
this._scrollItem = itemNode._listId != null ? itemNode : ev.target;
|
||
}
|
||
}
|
||
//触摸抬起时..
|
||
_onTouchUp() {
|
||
// Utils.Log("滚动容器 _onTouchUp")
|
||
this._scrollPos = null;
|
||
this._scrollItem = null;
|
||
}
|
||
|
||
_onTouchCancelled(ev, captureListeners) {
|
||
// Utils.Log("滚动容器 _onTouchCancelled")
|
||
this._scrollPos = null;
|
||
this._scrollItem = null;
|
||
}
|
||
//当尺寸改变
|
||
_onSizeChanged() {
|
||
// Utils.Log("滚动容器 _onSizeChanged")
|
||
if (this.checkInited(false))
|
||
this._onScrolling();
|
||
}
|
||
//当Item自适应
|
||
_onItemAdaptive(item) {
|
||
if (this.checkInited(false)) {
|
||
let uitf = item.getComponent(UITransform)
|
||
if (
|
||
(!this._sizeType && uitf.width != this._itemSize.width)
|
||
|| (this._sizeType && uitf.height != this._itemSize.height)
|
||
) {
|
||
if (!this._customSize)
|
||
this._customSize = {};
|
||
let val = this._sizeType ? uitf.height : uitf.width;
|
||
if (this._customSize[item._listId] != val) {
|
||
this._customSize[item._listId] = val;
|
||
this._resizeContent();
|
||
this.updateAll();
|
||
// 如果当前正在运行 scrollTo,肯定会不准确,在这里做修正
|
||
if (!isNaN(this._scrollToListId)) {
|
||
this._scrollPos = null;
|
||
this.unschedule(this._scrollToSo);
|
||
this.scrollTo(this._scrollToListId, Math.max(0, this._scrollToEndTime - ((new Date()).getTime() / 1000)));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//Update..
|
||
update() {
|
||
|
||
}
|
||
/**
|
||
* 创建或更新Item(虚拟列表用)
|
||
* @param {Object} data 数据
|
||
*/
|
||
_createOrUpdateItem(data: any) {
|
||
let item: any = this.getItemByListId(data.id);
|
||
if (!item) { //如果不存在
|
||
let canGet: boolean = this._pool.size() > 0;
|
||
if (canGet) {
|
||
item = this._pool.get();
|
||
} else {
|
||
item = instantiate(this._itemTmp);
|
||
}
|
||
|
||
if (item && item._listId != data.id) {
|
||
item._listId = data.id;
|
||
item.getComponent(UITransform).setContentSize(this._itemSize);
|
||
//命名
|
||
if (!isNaN(data.id)){
|
||
item.name = `svCell_${data.id}`
|
||
}
|
||
}
|
||
item.setPosition(v3(data.x, data.y));
|
||
this._resetItemSize(item);
|
||
this.content.addChild(item);
|
||
if (canGet && this._needUpdateWidget) {
|
||
let widget: Widget = item.getComponent(Widget);
|
||
if (widget)
|
||
widget.updateAlignment();
|
||
}
|
||
item.setSiblingIndex(this.content.children.length - 1);
|
||
|
||
let listItem: ScrollviewListItem = item.getComponent(ScrollviewListItem);
|
||
item['listItem'] = listItem;
|
||
if (listItem) {
|
||
listItem.listId = data.id;
|
||
listItem.list = this;
|
||
listItem._registerEvent();
|
||
}
|
||
if (this.renderEvent && item) {
|
||
EventHandler.emitEvents([this.renderEvent], item, data.id % this._actualNumItems);
|
||
}
|
||
} else if (this._forceUpdate && this.renderEvent) { //强制更新
|
||
item.setPosition(v3(data.x, data.y));
|
||
this._resetItemSize(item);
|
||
if (this.renderEvent && item) {
|
||
EventHandler.emitEvents([this.renderEvent], item, data.id % this._actualNumItems);
|
||
}
|
||
}
|
||
this._resetItemSize(item);
|
||
|
||
if (this._lastDisplayData.indexOf(data.id) < 0) {
|
||
this._lastDisplayData.push(data.id);
|
||
}
|
||
}
|
||
//创建或更新Item(非虚拟列表用)
|
||
_createOrUpdateItem2(listId: number) {
|
||
let item: any = this.content.children[listId];
|
||
let listItem: ScrollviewListItem;
|
||
if (!item) { //如果不存在
|
||
item = instantiate(this._itemTmp);
|
||
item._listId = listId;
|
||
this.content.addChild(item);
|
||
listItem = item.getComponent(ScrollviewListItem);
|
||
item['listItem'] = listItem;
|
||
if (listItem) {
|
||
listItem.listId = listId;
|
||
listItem.list = this;
|
||
listItem._registerEvent();
|
||
}
|
||
if (this.renderEvent && item) {
|
||
Component.EventHandler.emitEvents([this.renderEvent], item, listId);
|
||
}
|
||
} else if (this._forceUpdate && this.renderEvent) { //强制更新
|
||
item._listId = listId;
|
||
if (listItem)
|
||
listItem.listId = listId;
|
||
if (this.renderEvent && item) {
|
||
Component.EventHandler.emitEvents([this.renderEvent], item, listId);
|
||
}
|
||
}
|
||
if (this._lastDisplayData.indexOf(listId) < 0) {
|
||
this._lastDisplayData.push(listId);
|
||
}
|
||
}
|
||
|
||
//仅虚拟列表用
|
||
_resetItemSize(item: any) {
|
||
return;
|
||
let size: number;
|
||
if (this._customSize && this._customSize[item._listId]) {
|
||
size = this._customSize[item._listId];
|
||
} else {
|
||
if (this._colLineNum > 1)
|
||
item.getComponent(UITransform).setContentSize(this._itemSize);
|
||
else
|
||
size = this._sizeType ? this._itemSize.height : this._itemSize.width;
|
||
}
|
||
if (size) {
|
||
let uitf = item.getComponent(UITransform)
|
||
if (this._sizeType)
|
||
uitf.height = size;
|
||
else
|
||
uitf.width = size;
|
||
}
|
||
}
|
||
/**
|
||
* 更新Item位置
|
||
* @param {Number||Node} listIdOrItem
|
||
*/
|
||
_updateItemPos(listIdOrItem: any) {
|
||
let item: any = isNaN(listIdOrItem) ? listIdOrItem : this.getItemByListId(listIdOrItem);
|
||
let pos: any = this.getItemPos(item._listId);
|
||
item.setPosition(pos.x, pos.y);
|
||
}
|
||
/**
|
||
* 设置多选
|
||
* @param {Array} args 可以是单个listId,也可是个listId数组
|
||
* @param {Boolean} bool 值,如果为null的话,则直接用args覆盖
|
||
*/
|
||
setMultSelected(args: any, bool: boolean) {
|
||
if (!this.checkInited())
|
||
return;
|
||
if (!Array.isArray(args)) {
|
||
args = [args];
|
||
}
|
||
if (bool == null) {
|
||
this.multSelected = args;
|
||
} else {
|
||
let listId: number, sub: number;
|
||
if (bool) {
|
||
for (let n: number = args.length - 1; n >= 0; n--) {
|
||
listId = args[n];
|
||
sub = this.multSelected.indexOf(listId);
|
||
if (sub < 0) {
|
||
this.multSelected.push(listId);
|
||
}
|
||
}
|
||
} else {
|
||
for (let n: number = args.length - 1; n >= 0; n--) {
|
||
listId = args[n];
|
||
sub = this.multSelected.indexOf(listId);
|
||
if (sub >= 0) {
|
||
this.multSelected.splice(sub, 1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
this._forceUpdate = true;
|
||
this._onScrolling();
|
||
}
|
||
/**
|
||
* 更新指定的Item
|
||
* @param {Array} args 单个listId,或者数组
|
||
* @returns
|
||
*/
|
||
updateItem(args: any) {
|
||
if (!this.checkInited())
|
||
return;
|
||
if (!Array.isArray(args)) {
|
||
args = [args];
|
||
}
|
||
for (let n: number = 0, len: number = args.length; n < len; n++) {
|
||
let listId: number = args[n];
|
||
let item: any = this.getItemByListId(listId);
|
||
if (item)
|
||
Component.EventHandler.emitEvents([this.renderEvent], item, listId % this._actualNumItems);
|
||
}
|
||
}
|
||
/**
|
||
* 更新全部
|
||
*/
|
||
updateAll() {
|
||
if (!this.checkInited())
|
||
return;
|
||
this.numItems = this.numItems;
|
||
}
|
||
/**
|
||
* 根据ListID获取Item
|
||
* @param {Number} listId
|
||
* @returns
|
||
*/
|
||
getItemByListId(listId: number) {
|
||
if (this.content == null) return null;
|
||
for (let n: number = this.content.children.length - 1; n >= 0; n--) {
|
||
let item: any = this.content.children[n];
|
||
if (item._listId == listId)
|
||
return item;
|
||
}
|
||
}
|
||
/**
|
||
* 获取在显示区域外的Item
|
||
* @returns
|
||
*/
|
||
_getOutsideItem() {
|
||
let item: any;
|
||
let result: any[] = [];
|
||
for (let n: number = this.content.children.length - 1; n >= 0; n--) {
|
||
item = this.content.children[n];
|
||
let isHas: boolean = false;
|
||
for (let idx in this.displayData) {
|
||
if (this.displayData[idx].id == item._listId) {
|
||
isHas = true;
|
||
continue;
|
||
}
|
||
}
|
||
if (!isHas) {
|
||
result.push(item);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
//删除显示区域以外的Item
|
||
_delRedundantItem() {
|
||
if (this._virtual) {
|
||
let arr: any[] = this._getOutsideItem();
|
||
for (let n: number = arr.length - 1; n >= 0; n--) {
|
||
let item: any = arr[n];
|
||
if (this._scrollItem && item._listId == this._scrollItem._listId)
|
||
continue;
|
||
this._pool.put(item);
|
||
for (let m: number = this._lastDisplayData.length - 1; m >= 0; m--) {
|
||
if (this._lastDisplayData[m] == item._listId) {
|
||
this._lastDisplayData.splice(m, 1);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
while (this.content.children.length > this._numItems) {
|
||
this._delSingleItem(this.content.children[this.content.children.length - 1]);
|
||
}
|
||
}
|
||
}
|
||
//删除单个Item
|
||
_delSingleItem(item: any) {
|
||
item.removeFromParent();
|
||
if (item.destroy)
|
||
item.destroy();
|
||
item = null;
|
||
}
|
||
/**
|
||
* 动效删除Item(此方法只适用于虚拟列表,即_virtual=true)
|
||
* 一定要在回调函数里重新设置新的numItems进行刷新,毕竟本List是靠数据驱动的。
|
||
*/
|
||
aniDelItem(listId: number, callFunc: Function, aniType: number) {
|
||
|
||
if (!this.checkInited() || !this._virtual)
|
||
return //hg_utils.hgLog('This function is not allowed to be called!');
|
||
|
||
if (this._aniDelRuning)
|
||
return //hg_utils.hgLog('Please wait for the current deletion to finish!');
|
||
|
||
let item: any = this.getItemByListId(listId);
|
||
let listItem: ScrollviewListItem;
|
||
if (!item) {
|
||
callFunc(listId);
|
||
return;
|
||
} else {
|
||
listItem = item.getComponent(ScrollviewListItem);
|
||
}
|
||
this._aniDelRuning = true;
|
||
let curLastId: number = this.displayData[this.displayData.length - 1].id;
|
||
let resetSelectedId: boolean = listItem.selected;
|
||
listItem.showAni(aniType, () => {
|
||
//判断有没有下一个,如果有的话,创建粗来
|
||
let newId: number;
|
||
if (curLastId < this._numItems - 2) {
|
||
newId = curLastId + 1;
|
||
}
|
||
if (newId != null) {
|
||
let newData: any = this._calcItemPos(newId);
|
||
this.displayData.push(newData);
|
||
if (this._virtual)
|
||
this._createOrUpdateItem(newData);
|
||
else
|
||
this._createOrUpdateItem2(newId);
|
||
} else{
|
||
this._numItems--;
|
||
}
|
||
if (this._customSize) {
|
||
if (this._customSize[listId])
|
||
delete this._customSize[listId];
|
||
let newCustomSize: any = {};
|
||
let size: number;
|
||
for (let id in this._customSize) {
|
||
size = this._customSize[id];
|
||
let idNumber: number = parseInt(id);
|
||
newCustomSize[idNumber - (idNumber >= listId ? 1 : 0)] = size;
|
||
}
|
||
this._customSize = newCustomSize;
|
||
}
|
||
//后面的Item向前怼的动效
|
||
let sec: number = .2333;
|
||
let acts: any[], haveCB: boolean;
|
||
for (let n: number = newId != null ? newId : curLastId; n >= listId + 1; n--) {
|
||
item = this.getItemByListId(n);
|
||
if (item) {
|
||
let posData: any = this._calcItemPos(n - 1);
|
||
let a_num = 0
|
||
let act = tween(item);
|
||
act.to(sec, {position: v3(posData.x, posData.y)})
|
||
a_num += 1
|
||
// acts = [
|
||
// cc.moveTo(sec, cc.v2(posData.x, posData.y)),
|
||
// ];
|
||
if (n <= listId + 1) {
|
||
haveCB = true;
|
||
act.call(()=>{
|
||
this._aniDelRuning = false;
|
||
callFunc(listId);
|
||
})
|
||
a_num += 1
|
||
// acts.push(cc.callFunc(() => {
|
||
// this._aniDelRuning = false;
|
||
// callFunc(listId);
|
||
// }));
|
||
}
|
||
if (a_num > 0)
|
||
act.start()
|
||
}
|
||
}
|
||
if (!haveCB) {
|
||
this._aniDelRuning = false;
|
||
callFunc(listId);
|
||
}
|
||
}, true);
|
||
}
|
||
/**
|
||
* 滚动到..
|
||
* @param {Number} listId 索引(如果<0,则滚到首个Item位置,如果>=_numItems,则滚到最末Item位置)
|
||
* @param {Number} timeInSecond 时间
|
||
* @param {Number} offset 索引目标位置偏移,0-1
|
||
* @param {Boolean} overStress 滚动后是否强调该Item(这只是个实验功能)
|
||
*/
|
||
scrollTo(listId: number, timeInSecond: number = .5, offset: number = null, overStress: boolean = false) {
|
||
if (!this.checkInited(false))
|
||
return;
|
||
if (timeInSecond == null) //默认0.5
|
||
timeInSecond = .5;
|
||
else if (timeInSecond < 0)
|
||
timeInSecond = 0;
|
||
if (listId < 0)
|
||
listId = 0;
|
||
else if (listId >= this._numItems)
|
||
listId = this._numItems - 1;
|
||
// 以防设置了numItems之后layout的尺寸还未更新
|
||
if (!this._virtual && this._layout && this._layout.enabled)
|
||
this._layout.updateLayout();
|
||
|
||
let pos: any = this.getItemPos(listId);
|
||
let targetX: number, targetY: number;
|
||
|
||
switch (this._alignCalcType) {
|
||
case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
|
||
targetX = pos.left;
|
||
if (offset != null)
|
||
targetX -= this._uitf.width * offset;
|
||
else
|
||
targetX -= this._leftGap;
|
||
pos = v2(targetX, 0);
|
||
break;
|
||
case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
|
||
targetX = pos.right - this._uitf.width;
|
||
if (offset != null)
|
||
targetX += this._uitf.width * offset;
|
||
else
|
||
targetX += this._rightGap;
|
||
pos = v2(targetX + this.contentUITF.width, 0);
|
||
break;
|
||
case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
|
||
targetY = pos.top;
|
||
if (offset != null)
|
||
targetY += this._uitf.height * offset;
|
||
else
|
||
targetY += this._topGap;
|
||
pos = v2(0, -targetY);
|
||
break;
|
||
case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
|
||
targetY = pos.bottom + this._uitf.height;
|
||
if (offset != null)
|
||
targetY -= this._uitf.height * offset;
|
||
else
|
||
targetY -= this._bottomGap;
|
||
pos = v2(0, -targetY + this.contentUITF.height);
|
||
break;
|
||
}
|
||
let viewPos: any = this.content.getPosition();
|
||
viewPos = Math.abs(this._sizeType ? viewPos.y : viewPos.x);
|
||
|
||
let comparePos = this._sizeType ? pos.y : pos.x;
|
||
let runScroll = Math.abs((this._scrollPos != null ? this._scrollPos : viewPos) - comparePos) > .5;
|
||
if (runScroll) {
|
||
this._scrollView.scrollToOffset(pos, timeInSecond);
|
||
this._scrollToListId = listId;
|
||
this._scrollToEndTime = ((new Date()).getTime() / 1000) + timeInSecond;
|
||
this._scrollToSo = this.scheduleOnce(() => {
|
||
if (!this._adheringBarrier) {
|
||
this.adhering = this._adheringBarrier = false;
|
||
}
|
||
this._scrollPos =
|
||
this._scrollToListId =
|
||
this._scrollToEndTime =
|
||
this._scrollToSo =
|
||
null;
|
||
if (overStress) {
|
||
let item = this.getItemByListId(listId);
|
||
if (item) {
|
||
tween(item)
|
||
.to(0.1, {scale: v3(1.05, 1.05, 1.05)})
|
||
.to(0.1, {scale: v3(1, 1, 1)})
|
||
.start()
|
||
}
|
||
}
|
||
}, timeInSecond + .1);
|
||
|
||
if (timeInSecond <= 0) {
|
||
this._onScrolling();
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* 计算当前滚动窗最近的Item
|
||
*/
|
||
_calcNearestItem() {
|
||
this.nearestListId = null;
|
||
let data: any, center: number;
|
||
|
||
if (this._virtual)
|
||
this._calcViewPos();
|
||
|
||
let vTop: number, vRight: number, vBottom: number, vLeft: number;
|
||
vTop = this.viewTop;
|
||
vRight = this.viewRight;
|
||
vBottom = this.viewBottom;
|
||
vLeft = this.viewLeft;
|
||
|
||
let breakFor: boolean = false;
|
||
for (let n = 0; n < this.content.children.length && !breakFor; n += this._colLineNum) {
|
||
data = this._virtual ? this.displayData[n] : this._calcExistItemPos(n);
|
||
if (!data)
|
||
break;
|
||
center = this._sizeType ? ((data.top + data.bottom) / 2) : (center = (data.left + data.right) / 2);
|
||
switch (this._alignCalcType) {
|
||
case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
|
||
if (data.right >= vLeft) {
|
||
this.nearestListId = data.id;
|
||
if (vLeft > center)
|
||
this.nearestListId += this._colLineNum;
|
||
breakFor = true;
|
||
}
|
||
break;
|
||
case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
|
||
if (data.left <= vRight) {
|
||
this.nearestListId = data.id;
|
||
if (vRight < center)
|
||
this.nearestListId += this._colLineNum;
|
||
breakFor = true;
|
||
}
|
||
break;
|
||
case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
|
||
if (data.bottom <= vTop) {
|
||
this.nearestListId = data.id;
|
||
if (vTop < center)
|
||
this.nearestListId += this._colLineNum;
|
||
breakFor = true;
|
||
}
|
||
break;
|
||
case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
|
||
if (data.top >= vBottom) {
|
||
this.nearestListId = data.id;
|
||
if (vBottom > center)
|
||
this.nearestListId += this._colLineNum;
|
||
breakFor = true;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
//判断最后一个Item。。。(哎,这些判断真心恶心,判断了前面的还要判断最后一个。。。一开始呢,就只有一个布局(单列布局),那时候代码才三百行,后来就想着完善啊,艹..这坑真深,现在这行数都一千五了= =||)
|
||
data = this._virtual ? this.displayData[this.displayItemNum - 1] : this._calcExistItemPos(this._numItems - 1);
|
||
if (data && data.id == this._numItems - 1) {
|
||
center = this._sizeType ? ((data.top + data.bottom) / 2) : (center = (data.left + data.right) / 2);
|
||
switch (this._alignCalcType) {
|
||
case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
|
||
if (vRight > center)
|
||
this.nearestListId = data.id;
|
||
break;
|
||
case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
|
||
if (vLeft < center)
|
||
this.nearestListId = data.id;
|
||
break;
|
||
case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
|
||
if (vBottom < center)
|
||
this.nearestListId = data.id;
|
||
break;
|
||
case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
|
||
if (vTop > center)
|
||
this.nearestListId = data.id;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
//计算 CustomSize(这个函数还是保留吧,某些罕见的情况的确还是需要手动计算customSize的)
|
||
calcCustomSize(numItems: number) {
|
||
if (!this.checkInited())
|
||
return;
|
||
if (!this._itemTmp)
|
||
return //hg_utils.hgLog('Unset template item!');
|
||
if (!this.renderEvent)
|
||
return //hg_utils.hgLog('Unset Render-Event!');
|
||
this._customSize = {};
|
||
let temp: any = instantiate(this._itemTmp);
|
||
this.content.addChild(temp);
|
||
let uitf = temp.getComponent(UITransform)
|
||
for (let n: number = 0; n < numItems; n++) {
|
||
Component.EventHandler.emitEvents([this.renderEvent], temp, n);
|
||
if (uitf.height != this._itemSize.height || uitf.width != this._itemSize.width) {
|
||
this._customSize[n] = this._sizeType ? uitf.height : uitf.width;
|
||
}
|
||
}
|
||
if (!Object.keys(this._customSize).length)
|
||
this._customSize = null;
|
||
temp.removeFromParent();
|
||
if (temp.destroy)
|
||
temp.destroy();
|
||
return this._customSize;
|
||
}
|
||
} |