VirtualList.ts 87 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144
  1. /******************************************
  2. * @author kL <klk0@qq.com>
  3. * @date 2020/12/9
  4. * @doc 列表组件.
  5. * @end
  6. ******************************************/
  7. const { ccclass, property, disallowMultiple, menu, executionOrder, requireComponent } = _decorator;
  8. import { Node, Component, Enum, tween, _decorator, EventHandler, Tween, ScrollView, Prefab, Layout, Vec2, Size, NodePool, isValid, instantiate, Vec3, Widget, UITransform, CCFloat, CCBoolean, CCInteger } from 'cc';
  9. import { DEV } from 'cc/env';
  10. import ListItem from './VirtualItem';
  11. enum TemplateType {
  12. NODE = 1,
  13. PREFAB = 2,
  14. }
  15. enum SlideType {
  16. NORMAL = 1,//普通
  17. ADHERING = 2,//粘附模式,将强制关闭滚动惯性
  18. PAGE = 3,//页面模式,将强制关闭滚动惯性
  19. }
  20. enum SelectedType {
  21. NONE = 0,
  22. SINGLE = 1,//单选
  23. MULT = 2,//多选
  24. }
  25. @ccclass
  26. @disallowMultiple()
  27. @requireComponent(ScrollView)
  28. //脚本生命周期回调的执行优先级。小于 0 的脚本将优先执行,大于 0 的脚本将最后执行。该优先级只对 onLoad, onEnable, start, update 和 lateUpdate 有效,对 onDisable 和 onDestroy 无效。
  29. @executionOrder(-5000)
  30. export default class VirtualList extends Component {
  31. //模板类型
  32. @property({ type: Enum(TemplateType), tooltip: DEV && '模板类型', })
  33. private templateType: TemplateType = TemplateType.NODE;
  34. //模板Item(Node)
  35. @property({
  36. type: Node,
  37. tooltip: DEV && '模板Item',
  38. visible() { return this.templateType == TemplateType.NODE; }
  39. })
  40. tmpNode: Node = null;
  41. //模板Item(Prefab)
  42. @property({
  43. type: Prefab,
  44. tooltip: DEV && '模板Item',
  45. visible() { return this.templateType == TemplateType.PREFAB; }
  46. })
  47. tmpPrefab: Prefab = null;
  48. //滑动模式
  49. @property({})
  50. private _slideMode: SlideType = SlideType.NORMAL;
  51. @property({
  52. type: Enum(SlideType),
  53. tooltip: DEV && '滑动模式'
  54. })
  55. set slideMode(val: SlideType) {
  56. this._slideMode = val;
  57. }
  58. get slideMode() {
  59. return this._slideMode;
  60. }
  61. //翻页作用距离
  62. @property({
  63. type: CCFloat,
  64. range: [0, 1, .1],
  65. tooltip: DEV && '翻页作用距离',
  66. slide: true,
  67. visible() { return this._slideMode == SlideType.PAGE; }
  68. })
  69. public pageDistance: number = .3;
  70. //页面改变事件
  71. @property({
  72. type: EventHandler,
  73. tooltip: DEV && '页面改变事件',
  74. visible() { return this._slideMode == SlideType.PAGE; }
  75. })
  76. private pageChangeEvent: EventHandler = new EventHandler();
  77. //是否为虚拟列表(动态列表)
  78. @property({})
  79. private _virtual: boolean = true;
  80. @property({
  81. type: CCBoolean,
  82. tooltip: DEV && '是否为虚拟列表(动态列表)'
  83. })
  84. set virtual(val: boolean) {
  85. if (val != null)
  86. this._virtual = val;
  87. if (!DEV && this._numItems != 0) {
  88. this._onScrolling();
  89. }
  90. }
  91. get virtual() {
  92. return this._virtual;
  93. }
  94. //是否为循环列表
  95. @property({
  96. tooltip: DEV && '是否为循环列表',
  97. visible() {
  98. let val: boolean = /*this.virtual &&*/ this.slideMode == SlideType.NORMAL;
  99. if (!val)
  100. this.cyclic = false;
  101. return val;
  102. }
  103. })
  104. public cyclic: boolean = false;
  105. //缺省居中
  106. @property({
  107. tooltip: DEV && 'Item数量不足以填满Content时,是否居中显示Item(不支持Grid布局)',
  108. visible() { return this.virtual; }
  109. })
  110. public lackCenter: boolean = false;
  111. //缺省可滑动
  112. @property({
  113. tooltip: DEV && 'Item数量不足以填满Content时,是否可滑动',
  114. visible() {
  115. let val: boolean = this.virtual && !this.lackCenter;
  116. if (!val)
  117. this.lackSlide = false;
  118. return val;
  119. }
  120. })
  121. public lackSlide: boolean = false;
  122. //刷新频率
  123. @property({ type: CCInteger })
  124. private _updateRate: number = 0;
  125. @property({
  126. type: CCInteger,
  127. range: [0, 6, 1],
  128. tooltip: DEV && '刷新频率(值越大刷新频率越低、性能越高)',
  129. slide: true,
  130. })
  131. set updateRate(val: number) {
  132. if (val >= 0 && val <= 6) {
  133. this._updateRate = val;
  134. }
  135. }
  136. get updateRate() {
  137. return this._updateRate;
  138. }
  139. //分帧渲染(每帧渲染的Item数量(<=0时关闭分帧渲染))
  140. @property({
  141. type: CCInteger,
  142. range: [0, 12, 1],
  143. tooltip: DEV && '逐帧渲染时,每帧渲染的Item数量(<=0时关闭分帧渲染)',
  144. slide: true,
  145. })
  146. public frameByFrameRenderNum: number = 0;
  147. //渲染事件(渲染器)
  148. @property({
  149. type: EventHandler,
  150. tooltip: DEV && '渲染事件(渲染器)',
  151. })
  152. public renderEvent: EventHandler = new EventHandler();
  153. //选择模式
  154. @property({
  155. type: Enum(SelectedType),
  156. tooltip: DEV && '选择模式'
  157. })
  158. public selectedMode: SelectedType = SelectedType.NONE;
  159. //触发选择事件
  160. @property({
  161. type: EventHandler,
  162. tooltip: DEV && '触发选择事件',
  163. visible() { return this.selectedMode > SelectedType.NONE; }
  164. })
  165. public selectedEvent: EventHandler = new EventHandler();
  166. @property({
  167. tooltip: DEV && '是否重复响应单选事件',
  168. visible() { return this.selectedMode == SelectedType.SINGLE; }
  169. })
  170. public repeatEventSingle: boolean = false;
  171. //当前选择id
  172. private _selectedId: number = -1;
  173. private _lastSelectedId: number;
  174. private multSelected: number[];
  175. set selectedId(val: number) {
  176. let t: any = this;
  177. let item: any;
  178. switch (t.selectedMode) {
  179. case SelectedType.SINGLE: {
  180. if (!t.repeatEventSingle && val == t._selectedId)
  181. return;
  182. item = t.getItemByListId(val);
  183. // if (!item && val >= 0)
  184. // return;
  185. let listItem: ListItem;
  186. if (t._selectedId >= 0)
  187. t._lastSelectedId = t._selectedId;
  188. else //如果<0则取消选择,把_lastSelectedId也置空吧,如果以后有特殊需求再改吧。
  189. t._lastSelectedId = null;
  190. t._selectedId = val;
  191. if (item) {
  192. listItem = item.getComponent(ListItem);
  193. listItem.selected = true;
  194. }
  195. if (t._lastSelectedId >= 0 && t._lastSelectedId != t._selectedId) {
  196. let lastItem: any = t.getItemByListId(t._lastSelectedId);
  197. if (lastItem) {
  198. lastItem.getComponent(ListItem).selected = false;
  199. }
  200. }
  201. if (t.selectedEvent) {
  202. EventHandler.emitEvents([t.selectedEvent], item, val % this._actualNumItems, t._lastSelectedId == null ? null : (t._lastSelectedId % this._actualNumItems));
  203. }
  204. break;
  205. }
  206. case SelectedType.MULT: {
  207. item = t.getItemByListId(val);
  208. if (!item)
  209. return;
  210. let listItem = item.getComponent(ListItem);
  211. if (t._selectedId >= 0)
  212. t._lastSelectedId = t._selectedId;
  213. t._selectedId = val;
  214. let bool: boolean = !listItem.selected;
  215. listItem.selected = bool;
  216. let sub: number = t.multSelected.indexOf(val);
  217. if (bool && sub < 0) {
  218. t.multSelected.push(val);
  219. } else if (!bool && sub >= 0) {
  220. t.multSelected.splice(sub, 1);
  221. }
  222. if (t.selectedEvent) {
  223. EventHandler.emitEvents([t.selectedEvent], item, val % this._actualNumItems, t._lastSelectedId == null ? null : (t._lastSelectedId % this._actualNumItems), bool);
  224. }
  225. break;
  226. }
  227. }
  228. }
  229. get selectedId() {
  230. return this._selectedId;
  231. }
  232. private _forceUpdate: boolean = false;
  233. private _align: number;
  234. private _horizontalDir: number;
  235. private _verticalDir: number;
  236. private _startAxis: number;
  237. private _alignCalcType: number;
  238. public content: Node;
  239. private _contentUt: UITransform;
  240. private firstListId: number;
  241. public displayItemNum: number;
  242. private _updateDone: boolean = true;
  243. private _updateCounter: number;
  244. public _actualNumItems: number;
  245. private _cyclicNum: number;
  246. private _cyclicPos1: number;
  247. private _cyclicPos2: number;
  248. //列表数量
  249. @property({
  250. serializable: false
  251. })
  252. private _numItems: number = 0;
  253. set numItems(val: number) {
  254. let t = this;
  255. if (!t.checkInited(false))
  256. return;
  257. if (val == null || val < 0) {
  258. console.error('numItems set the wrong::', val);
  259. return;
  260. }
  261. t._actualNumItems = t._numItems = val;
  262. t._forceUpdate = true;
  263. if (t._virtual) {
  264. t._resizeContent();
  265. if (t.cyclic) {
  266. t._numItems = t._cyclicNum * t._numItems;
  267. }
  268. t._onScrolling();
  269. if (!t.frameByFrameRenderNum && t.slideMode == SlideType.PAGE)
  270. t.curPageNum = t.nearestListId;
  271. } else {
  272. if (t.cyclic) {
  273. t._resizeContent();
  274. t._numItems = t._cyclicNum * t._numItems;
  275. }
  276. let layout: Layout = t.content.getComponent(Layout);
  277. if (layout) {
  278. layout.enabled = true;
  279. }
  280. t._delRedundantItem();
  281. t.firstListId = 0;
  282. if (t.frameByFrameRenderNum > 0) {
  283. //先渲染几个出来
  284. let len: number = t.frameByFrameRenderNum > t._numItems ? t._numItems : t.frameByFrameRenderNum;
  285. for (let n: number = 0; n < len; n++) {
  286. t._createOrUpdateItem2(n);
  287. }
  288. if (t.frameByFrameRenderNum < t._numItems) {
  289. t._updateCounter = t.frameByFrameRenderNum;
  290. t._updateDone = false;
  291. }
  292. } else {
  293. for (let n: number = 0; n < t._numItems; n++) {
  294. t._createOrUpdateItem2(n);
  295. }
  296. t.displayItemNum = t._numItems;
  297. }
  298. }
  299. }
  300. get numItems() {
  301. return this._actualNumItems;
  302. }
  303. private _inited: boolean = false;
  304. private _scrollView: ScrollView;
  305. get scrollView() {
  306. return this._scrollView;
  307. }
  308. private _layout: Layout;
  309. private _resizeMode: number;
  310. private _topGap: number;
  311. private _rightGap: number;
  312. private _bottomGap: number;
  313. private _leftGap: number;
  314. private _columnGap: number;
  315. private _lineGap: number;
  316. private _colLineNum: number;
  317. private _lastDisplayData: number[];
  318. public displayData: any[];
  319. private _pool: NodePool;
  320. private _itemTmp: any;
  321. private _itemTmpUt: UITransform;
  322. private _needUpdateWidget: boolean = false;
  323. private _itemSize: Size;
  324. private _sizeType: boolean;
  325. public _customSize: any;
  326. private frameCount: number;
  327. private _aniDelRuning: boolean = false;
  328. private _aniDelCB: Function;
  329. private _aniDelItem: any;
  330. private _aniDelBeforePos: Vec2;
  331. private _aniDelBeforeScale: number;
  332. private viewTop: number;
  333. private viewRight: number;
  334. private viewBottom: number;
  335. private viewLeft: number;
  336. private _doneAfterUpdate: boolean = false;
  337. private elasticTop: number;
  338. private elasticRight: number;
  339. private elasticBottom: number;
  340. private elasticLeft: number;
  341. private scrollToListId: number;
  342. private adhering: boolean = false;
  343. private _adheringBarrier: boolean = false;
  344. private nearestListId: number;
  345. public curPageNum: number = 0;
  346. private _beganPos: number;
  347. private _scrollPos: number;
  348. private _curScrollIsTouch: boolean;//当前滑动是否为手动
  349. private _scrollToListId: number;
  350. private _scrollToEndTime: number;
  351. private _scrollToSo: any;
  352. private _lack: boolean;
  353. private _allItemSize: number;
  354. private _allItemSizeNoEdge: number;
  355. private _scrollItem: any;//当前控制 ScrollView 滚动的 Item
  356. private _thisNodeUt: UITransform;
  357. //----------------------------------------------------------------------------
  358. onLoad() {
  359. this._init();
  360. }
  361. onDestroy() {
  362. let t: any = this;
  363. if (isValid(t._itemTmp))
  364. t._itemTmp.destroy();
  365. if (isValid(t.tmpNode))
  366. t.tmpNode.destroy();
  367. t._pool && t._pool.clear();
  368. }
  369. onEnable() {
  370. // if (!EDITOR)
  371. this._registerEvent();
  372. this._init();
  373. // 处理重新显示后,有可能上一次的动画移除还未播放完毕,导致动画卡住的问题
  374. if (this._aniDelRuning) {
  375. this._aniDelRuning = false;
  376. if (this._aniDelItem) {
  377. if (this._aniDelBeforePos) {
  378. this._aniDelItem.position = this._aniDelBeforePos;
  379. delete this._aniDelBeforePos;
  380. }
  381. if (this._aniDelBeforeScale) {
  382. this._aniDelItem.scale = this._aniDelBeforeScale;
  383. delete this._aniDelBeforeScale;
  384. }
  385. delete this._aniDelItem;
  386. }
  387. if (this._aniDelCB) {
  388. this._aniDelCB();
  389. delete this._aniDelCB;
  390. }
  391. }
  392. }
  393. onDisable() {
  394. // if (!EDITOR)
  395. this._unregisterEvent();
  396. }
  397. //注册事件
  398. _registerEvent() {
  399. let t: any = this;
  400. t.node.on(Node.EventType.TOUCH_START, t._onTouchStart, t);
  401. t.node.on('touch-up', t._onTouchUp, t);
  402. t.node.on(Node.EventType.TOUCH_CANCEL, t._onTouchCancelled, t);
  403. t.node.on('scroll-began', t._onScrollBegan, t);
  404. t.node.on('scroll-ended', t._onScrollEnded, t);
  405. t.node.on('scrolling', t._onScrolling, t);
  406. t.node.on(Node.EventType.SIZE_CHANGED, t._onSizeChanged, t);
  407. }
  408. //卸载事件
  409. _unregisterEvent() {
  410. let t: any = this;
  411. t.node.off(Node.EventType.TOUCH_START, t._onTouchStart, t);
  412. t.node.off('touch-up', t._onTouchUp, t);
  413. t.node.off(Node.EventType.TOUCH_CANCEL, t._onTouchCancelled, t);
  414. t.node.off('scroll-began', t._onScrollBegan, t);
  415. t.node.off('scroll-ended', t._onScrollEnded, t);
  416. t.node.off('scrolling', t._onScrolling, t);
  417. t.node.off(Node.EventType.SIZE_CHANGED, t._onSizeChanged, t);
  418. }
  419. //初始化各种..
  420. _init() {
  421. let t: any = this;
  422. if (t._inited)
  423. return;
  424. t._thisNodeUt = t.node.getComponent(UITransform);
  425. t._scrollView = t.node.getComponent(ScrollView);
  426. t.content = t._scrollView.content;
  427. t._contentUt = t.content.getComponent(UITransform);
  428. if (!t.content) {
  429. console.error(t.node.name + "'s ScrollView unset content!");
  430. return;
  431. }
  432. t._layout = t.content.getComponent(Layout);
  433. t._align = t._layout.type; //排列模式
  434. t._resizeMode = t._layout.resizeMode; //自适应模式
  435. t._startAxis = t._layout.startAxis;
  436. t._topGap = t._layout.paddingTop; //顶边距
  437. t._rightGap = t._layout.paddingRight; //右边距
  438. t._bottomGap = t._layout.paddingBottom; //底边距
  439. t._leftGap = t._layout.paddingLeft; //左边距
  440. t._columnGap = t._layout.spacingX; //列距
  441. t._lineGap = t._layout.spacingY; //行距
  442. t._colLineNum; //列数或行数(非GRID模式则=1,表示单列或单行);
  443. t._verticalDir = t._layout.verticalDirection; //垂直排列子节点的方向
  444. t._horizontalDir = t._layout.horizontalDirection; //水平排列子节点的方向
  445. t.setTemplateItem(instantiate(t.templateType == TemplateType.PREFAB ? t.tmpPrefab : t.tmpNode));
  446. // 特定的滑动模式处理
  447. if (t._slideMode == SlideType.ADHERING || t._slideMode == SlideType.PAGE) {
  448. t._scrollView.inertia = false;
  449. t._scrollView._onMouseWheel = function () {
  450. return;
  451. };
  452. }
  453. if (!t.virtual) // lackCenter 仅支持 Virtual 模式
  454. t.lackCenter = false;
  455. t._lastDisplayData = []; //最后一次刷新的数据
  456. t.displayData = []; //当前数据
  457. t._pool = new NodePool(); //这是个池子..
  458. t._forceUpdate = false; //是否强制更新
  459. t._updateCounter = 0; //当前分帧渲染帧数
  460. t._updateDone = true; //分帧渲染是否完成
  461. t.curPageNum = 0; //当前页数
  462. if (t.cyclic || 0) {
  463. t._scrollView._processAutoScrolling = this._processAutoScrolling.bind(t);
  464. t._scrollView._startBounceBackIfNeeded = function () {
  465. return false;
  466. }
  467. }
  468. switch (t._align) {
  469. case Layout.Type.HORIZONTAL: {
  470. switch (t._horizontalDir) {
  471. case Layout.HorizontalDirection.LEFT_TO_RIGHT:
  472. t._alignCalcType = 1;
  473. break;
  474. case Layout.HorizontalDirection.RIGHT_TO_LEFT:
  475. t._alignCalcType = 2;
  476. break;
  477. }
  478. break;
  479. }
  480. case Layout.Type.VERTICAL: {
  481. switch (t._verticalDir) {
  482. case Layout.VerticalDirection.TOP_TO_BOTTOM:
  483. t._alignCalcType = 3;
  484. break;
  485. case Layout.VerticalDirection.BOTTOM_TO_TOP:
  486. t._alignCalcType = 4;
  487. break;
  488. }
  489. break;
  490. }
  491. case Layout.Type.GRID: {
  492. switch (t._startAxis) {
  493. case Layout.AxisDirection.HORIZONTAL:
  494. switch (t._verticalDir) {
  495. case Layout.VerticalDirection.TOP_TO_BOTTOM:
  496. t._alignCalcType = 3;
  497. break;
  498. case Layout.VerticalDirection.BOTTOM_TO_TOP:
  499. t._alignCalcType = 4;
  500. break;
  501. }
  502. break;
  503. case Layout.AxisDirection.VERTICAL:
  504. switch (t._horizontalDir) {
  505. case Layout.HorizontalDirection.LEFT_TO_RIGHT:
  506. t._alignCalcType = 1;
  507. break;
  508. case Layout.HorizontalDirection.RIGHT_TO_LEFT:
  509. t._alignCalcType = 2;
  510. break;
  511. }
  512. break;
  513. }
  514. break;
  515. }
  516. }
  517. // 清空 content
  518. // t.content.children.forEach((child: Node) => {
  519. // child.removeFromParent();
  520. // if (child != t.tmpNode && child.isValid)
  521. // child.destroy();
  522. // });
  523. t.content.removeAllChildren();
  524. t._inited = true;
  525. }
  526. /**
  527. * 为了实现循环列表,必须覆写cc.ScrollView的某些函数
  528. * @param {Number} dt
  529. */
  530. _processAutoScrolling(dt: number) {
  531. // ------------- scroll-view 里定义的一些常量 -------------
  532. const OUT_OF_BOUNDARY_BREAKING_FACTOR = 0.05;
  533. const EPSILON = 1e-4;
  534. const ZERO = new Vec3();
  535. const quintEaseOut = (time: number) => {
  536. time -= 1;
  537. return (time * time * time * time * time + 1);
  538. };
  539. // ------------- scroll-view 里定义的一些常量 -------------
  540. let sv: ScrollView = this._scrollView;
  541. const isAutoScrollBrake = sv['_isNecessaryAutoScrollBrake']();
  542. const brakingFactor = isAutoScrollBrake ? OUT_OF_BOUNDARY_BREAKING_FACTOR : 1;
  543. sv['_autoScrollAccumulatedTime'] += dt * (1 / brakingFactor);
  544. let percentage = Math.min(1, sv['_autoScrollAccumulatedTime'] / sv['_autoScrollTotalTime']);
  545. if (sv['_autoScrollAttenuate']) {
  546. percentage = quintEaseOut(percentage);
  547. }
  548. const clonedAutoScrollTargetDelta = sv['_autoScrollTargetDelta'].clone();
  549. clonedAutoScrollTargetDelta.multiplyScalar(percentage);
  550. const clonedAutoScrollStartPosition = sv['_autoScrollStartPosition'].clone();
  551. clonedAutoScrollStartPosition.add(clonedAutoScrollTargetDelta);
  552. let reachedEnd = Math.abs(percentage - 1) <= EPSILON;
  553. const fireEvent = Math.abs(percentage - 1) <= sv['getScrollEndedEventTiming']();
  554. if (fireEvent && !sv['_isScrollEndedWithThresholdEventFired']) {
  555. sv['_dispatchEvent'](ScrollView.EventType.SCROLL_ENG_WITH_THRESHOLD);
  556. sv['_isScrollEndedWithThresholdEventFired'] = true;
  557. }
  558. if (sv['elastic']) {
  559. const brakeOffsetPosition = clonedAutoScrollStartPosition.clone();
  560. brakeOffsetPosition.subtract(sv['_autoScrollBrakingStartPosition']);
  561. if (isAutoScrollBrake) {
  562. brakeOffsetPosition.multiplyScalar(brakingFactor);
  563. }
  564. clonedAutoScrollStartPosition.set(sv['_autoScrollBrakingStartPosition']);
  565. clonedAutoScrollStartPosition.add(brakeOffsetPosition);
  566. } else {
  567. const moveDelta = clonedAutoScrollStartPosition.clone();
  568. moveDelta.subtract(sv['_getContentPosition']());
  569. const outOfBoundary = sv['_getHowMuchOutOfBoundary'](moveDelta);
  570. if (!outOfBoundary.equals(ZERO, EPSILON)) {
  571. clonedAutoScrollStartPosition.add(outOfBoundary);
  572. reachedEnd = true;
  573. }
  574. }
  575. if (reachedEnd) {
  576. sv['_autoScrolling'] = false;
  577. }
  578. const deltaMove = new Vec3(clonedAutoScrollStartPosition);
  579. deltaMove.subtract(sv['_getContentPosition']());
  580. sv['_clampDelta'](deltaMove);
  581. sv['_moveContent'](deltaMove, reachedEnd);
  582. sv['_dispatchEvent'](ScrollView.EventType.SCROLLING);
  583. if (!sv['_autoScrolling']) {
  584. sv['_isBouncing'] = false;
  585. sv['_scrolling'] = false;
  586. sv['_dispatchEvent'](ScrollView.EventType.SCROLL_ENDED);
  587. }
  588. }
  589. //设置模板Item
  590. setTemplateItem(item: any) {
  591. if (!item)
  592. return;
  593. let t: any = this;
  594. t._itemTmp = item;
  595. t._itemTmpUt = item.getComponent(UITransform);
  596. if (t._resizeMode == Layout.ResizeMode.CHILDREN)
  597. t._itemSize = t._layout.cellSize;
  598. else {
  599. let itemUt: UITransform = item.getComponent(UITransform);
  600. t._itemSize = new Size(itemUt.width, itemUt.height);
  601. }
  602. //获取ListItem,如果没有就取消选择模式
  603. let com: any = item.getComponent(ListItem);
  604. let remove = false;
  605. if (!com)
  606. remove = true;
  607. // if (com) {
  608. // if (!com._btnCom && !item.getComponent(cc.Button)) {
  609. // remove = true;
  610. // }
  611. // }
  612. if (remove) {
  613. t.selectedMode = SelectedType.NONE;
  614. }
  615. com = item.getComponent(Widget);
  616. if (com && com.enabled) {
  617. t._needUpdateWidget = true;
  618. }
  619. if (t.selectedMode == SelectedType.MULT)
  620. t.multSelected = [];
  621. switch (t._align) {
  622. case Layout.Type.HORIZONTAL:
  623. t._colLineNum = 1;
  624. t._sizeType = false;
  625. break;
  626. case Layout.Type.VERTICAL:
  627. t._colLineNum = 1;
  628. t._sizeType = true;
  629. break;
  630. case Layout.Type.GRID:
  631. switch (t._startAxis) {
  632. case Layout.AxisDirection.HORIZONTAL:
  633. //计算列数
  634. let trimW: number = t._contentUt.width - t._leftGap - t._rightGap;
  635. t._colLineNum = Math.floor((trimW + t._columnGap) / (t._itemSize.width + t._columnGap));
  636. t._sizeType = true;
  637. break;
  638. case Layout.AxisDirection.VERTICAL:
  639. //计算行数
  640. let trimH: number = t._contentUt.height - t._topGap - t._bottomGap;
  641. t._colLineNum = Math.floor((trimH + t._lineGap) / (t._itemSize.height + t._lineGap));
  642. t._sizeType = false;
  643. break;
  644. }
  645. break;
  646. }
  647. }
  648. /**
  649. * 检查是否初始化
  650. * @param {Boolean} printLog 是否打印错误信息
  651. * @returns
  652. */
  653. checkInited(printLog: boolean = true) {
  654. if (!this._inited) {
  655. if (printLog)
  656. console.error('List initialization not completed!');
  657. return false;
  658. }
  659. return true;
  660. }
  661. //禁用 Layout 组件,自行计算 Content Size
  662. _resizeContent() {
  663. let t: any = this;
  664. let result: number;
  665. switch (t._align) {
  666. case Layout.Type.HORIZONTAL: {
  667. if (t._customSize) {
  668. let fixed: any = t._getFixedSize(null);
  669. result = t._leftGap + fixed.val + (t._itemSize.width * (t._numItems - fixed.count)) + (t._columnGap * (t._numItems - 1)) + t._rightGap;
  670. } else {
  671. result = t._leftGap + (t._itemSize.width * t._numItems) + (t._columnGap * (t._numItems - 1)) + t._rightGap;
  672. }
  673. break;
  674. }
  675. case Layout.Type.VERTICAL: {
  676. if (t._customSize) {
  677. let fixed: any = t._getFixedSize(null);
  678. result = t._topGap + fixed.val + (t._itemSize.height * (t._numItems - fixed.count)) + (t._lineGap * (t._numItems - 1)) + t._bottomGap;
  679. } else {
  680. result = t._topGap + (t._itemSize.height * t._numItems) + (t._lineGap * (t._numItems - 1)) + t._bottomGap;
  681. }
  682. break;
  683. }
  684. case Layout.Type.GRID: {
  685. //网格模式不支持居中
  686. if (t.lackCenter)
  687. t.lackCenter = false;
  688. switch (t._startAxis) {
  689. case Layout.AxisDirection.HORIZONTAL:
  690. let lineNum: number = Math.ceil(t._numItems / t._colLineNum);
  691. result = t._topGap + (t._itemSize.height * lineNum) + (t._lineGap * (lineNum - 1)) + t._bottomGap;
  692. break;
  693. case Layout.AxisDirection.VERTICAL:
  694. let colNum: number = Math.ceil(t._numItems / t._colLineNum);
  695. result = t._leftGap + (t._itemSize.width * colNum) + (t._columnGap * (colNum - 1)) + t._rightGap;
  696. break;
  697. }
  698. break;
  699. }
  700. }
  701. let layout: Layout = t.content.getComponent(Layout);
  702. if (layout)
  703. layout.enabled = false;
  704. t._allItemSize = result;
  705. t._allItemSizeNoEdge = t._allItemSize - (t._sizeType ? (t._topGap + t._bottomGap) : (t._leftGap + t._rightGap));
  706. if (t.cyclic) {
  707. let totalSize: number = (t._sizeType ? t._thisNodeUt.height : t._thisNodeUt.width);
  708. t._cyclicPos1 = 0;
  709. totalSize -= t._cyclicPos1;
  710. t._cyclicNum = Math.ceil(totalSize / t._allItemSizeNoEdge) + 1;
  711. let spacing: number = t._sizeType ? t._lineGap : t._columnGap;
  712. t._cyclicPos2 = t._cyclicPos1 + t._allItemSizeNoEdge + spacing;
  713. t._cyclicAllItemSize = t._allItemSize + (t._allItemSizeNoEdge * (t._cyclicNum - 1)) + (spacing * (t._cyclicNum - 1));
  714. t._cycilcAllItemSizeNoEdge = t._allItemSizeNoEdge * t._cyclicNum;
  715. t._cycilcAllItemSizeNoEdge += spacing * (t._cyclicNum - 1);
  716. // cc.log('_cyclicNum ->', t._cyclicNum, t._allItemSizeNoEdge, t._allItemSize, t._cyclicPos1, t._cyclicPos2);
  717. }
  718. t._lack = !t.cyclic && t._allItemSize < (t._sizeType ? t._thisNodeUt.height : t._thisNodeUt.width);
  719. let slideOffset: number = ((!t._lack || !t.lackCenter) && t.lackSlide) ? 0 : .1;
  720. let targetWH: number = t._lack ? ((t._sizeType ? t._thisNodeUt.height : t._thisNodeUt.width) - slideOffset) : (t.cyclic ? t._cyclicAllItemSize : t._allItemSize);
  721. if (targetWH < 0)
  722. targetWH = 0;
  723. if (t._sizeType) {
  724. t._contentUt.height = targetWH;
  725. } else {
  726. t._contentUt.width = targetWH;
  727. }
  728. // cc.log('_resizeContent() numItems =', t._numItems, ',content =', t.content);
  729. }
  730. //滚动进行时...
  731. _onScrolling(ev: Event = null) {
  732. if (this.frameCount == null)
  733. this.frameCount = this._updateRate;
  734. if (!this._forceUpdate && (ev && ev.type != 'scroll-ended') && this.frameCount > 0) {
  735. this.frameCount--;
  736. return;
  737. } else
  738. this.frameCount = this._updateRate;
  739. if (this._aniDelRuning)
  740. return;
  741. //循环列表处理
  742. if (this.cyclic) {
  743. let scrollPos: any = this.content.getPosition();
  744. scrollPos = this._sizeType ? scrollPos.y : scrollPos.x;
  745. let addVal = this._allItemSizeNoEdge + (this._sizeType ? this._lineGap : this._columnGap);
  746. let add: any = this._sizeType ? new Vec3(0, addVal, 0) : new Vec3(addVal, 0, 0);
  747. let contentPos = this.content.getPosition();
  748. switch (this._alignCalcType) {
  749. case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
  750. if (scrollPos > -this._cyclicPos1) {
  751. contentPos.set(-this._cyclicPos2, contentPos.y, contentPos.z);
  752. this.content.setPosition(contentPos);
  753. if (this._scrollView.isAutoScrolling()) {
  754. this._scrollView['_autoScrollStartPosition'] = this._scrollView['_autoScrollStartPosition'].subtract(add);
  755. }
  756. // if (this._beganPos) {
  757. // this._beganPos += add;
  758. // }
  759. } else if (scrollPos < -this._cyclicPos2) {
  760. contentPos.set(-this._cyclicPos1, contentPos.y, contentPos.z);
  761. this.content.setPosition(contentPos);
  762. if (this._scrollView.isAutoScrolling()) {
  763. this._scrollView['_autoScrollStartPosition'] = this._scrollView['_autoScrollStartPosition'].add(add);
  764. }
  765. // if (this._beganPos) {
  766. // this._beganPos -= add;
  767. // }
  768. }
  769. break;
  770. case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
  771. if (scrollPos < this._cyclicPos1) {
  772. contentPos.set(this._cyclicPos2, contentPos.y, contentPos.z);
  773. this.content.setPosition(contentPos);
  774. if (this._scrollView.isAutoScrolling()) {
  775. this._scrollView['_autoScrollStartPosition'] = this._scrollView['_autoScrollStartPosition'].add(add);
  776. }
  777. } else if (scrollPos > this._cyclicPos2) {
  778. contentPos.set(this._cyclicPos1, contentPos.y, contentPos.z);
  779. this.content.setPosition(contentPos);
  780. if (this._scrollView.isAutoScrolling()) {
  781. this._scrollView['_autoScrollStartPosition'] = this._scrollView['_autoScrollStartPosition'].subtract(add);
  782. }
  783. }
  784. break;
  785. case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
  786. if (scrollPos < this._cyclicPos1) {
  787. contentPos.set(contentPos.x, this._cyclicPos2, contentPos.z);
  788. this.content.setPosition(contentPos);
  789. if (this._scrollView.isAutoScrolling()) {
  790. this._scrollView['_autoScrollStartPosition'] = this._scrollView['_autoScrollStartPosition'].add(add);
  791. }
  792. } else if (scrollPos > this._cyclicPos2) {
  793. contentPos.set(contentPos.x, this._cyclicPos1, contentPos.z);
  794. this.content.setPosition(contentPos);
  795. if (this._scrollView.isAutoScrolling()) {
  796. this._scrollView['_autoScrollStartPosition'] = this._scrollView['_autoScrollStartPosition'].subtract(add);
  797. }
  798. }
  799. break;
  800. case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
  801. if (scrollPos > -this._cyclicPos1) {
  802. contentPos.set(contentPos.x, -this._cyclicPos2, contentPos.z);
  803. this.content.setPosition(contentPos);
  804. if (this._scrollView.isAutoScrolling()) {
  805. this._scrollView['_autoScrollStartPosition'] = this._scrollView['_autoScrollStartPosition'].subtract(add);
  806. }
  807. } else if (scrollPos < -this._cyclicPos2) {
  808. contentPos.set(contentPos.x, -this._cyclicPos1, contentPos.z);
  809. this.content.setPosition(contentPos);
  810. if (this._scrollView.isAutoScrolling()) {
  811. this._scrollView['_autoScrollStartPosition'] = this._scrollView['_autoScrollStartPosition'].add(add);
  812. }
  813. }
  814. break;
  815. }
  816. }
  817. this._calcViewPos();
  818. let vTop: number, vRight: number, vBottom: number, vLeft: number;
  819. if (this._sizeType) {
  820. vTop = this.viewTop;
  821. vBottom = this.viewBottom;
  822. } else {
  823. vRight = this.viewRight;
  824. vLeft = this.viewLeft;
  825. }
  826. if (this._virtual) {
  827. this.displayData = [];
  828. let itemPos: any;
  829. let curId: number = 0;
  830. let endId: number = this._numItems - 1;
  831. if (this._customSize) {
  832. let breakFor: boolean = false;
  833. //如果该item的位置在可视区域内,就推入displayData
  834. for (; curId <= endId && !breakFor; curId++) {
  835. itemPos = this._calcItemPos(curId);
  836. switch (this._align) {
  837. case Layout.Type.HORIZONTAL:
  838. if (itemPos.right >= vLeft && itemPos.left <= vRight) {
  839. this.displayData.push(itemPos);
  840. } else if (curId != 0 && this.displayData.length > 0) {
  841. breakFor = true;
  842. }
  843. break;
  844. case Layout.Type.VERTICAL:
  845. if (itemPos.bottom <= vTop && itemPos.top >= vBottom) {
  846. this.displayData.push(itemPos);
  847. } else if (curId != 0 && this.displayData.length > 0) {
  848. breakFor = true;
  849. }
  850. break;
  851. case Layout.Type.GRID:
  852. switch (this._startAxis) {
  853. case Layout.AxisDirection.HORIZONTAL:
  854. if (itemPos.bottom <= vTop && itemPos.top >= vBottom) {
  855. this.displayData.push(itemPos);
  856. } else if (curId != 0 && this.displayData.length > 0) {
  857. breakFor = true;
  858. }
  859. break;
  860. case Layout.AxisDirection.VERTICAL:
  861. if (itemPos.right >= vLeft && itemPos.left <= vRight) {
  862. this.displayData.push(itemPos);
  863. } else if (curId != 0 && this.displayData.length > 0) {
  864. breakFor = true;
  865. }
  866. break;
  867. }
  868. break;
  869. }
  870. }
  871. } else {
  872. let ww: number = this._itemSize.width + this._columnGap;
  873. let hh: number = this._itemSize.height + this._lineGap;
  874. switch (this._alignCalcType) {
  875. case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
  876. curId = (vLeft - this._leftGap) / ww;
  877. endId = (vRight - this._leftGap) / ww;
  878. break;
  879. case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
  880. curId = (-vRight - this._rightGap) / ww;
  881. endId = (-vLeft - this._rightGap) / ww;
  882. break;
  883. case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
  884. curId = (-vTop - this._topGap) / hh;
  885. endId = (-vBottom - this._topGap) / hh;
  886. break;
  887. case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
  888. curId = (vBottom - this._bottomGap) / hh;
  889. endId = (vTop - this._bottomGap) / hh;
  890. break;
  891. }
  892. curId = Math.floor(curId) * this._colLineNum;
  893. endId = Math.ceil(endId) * this._colLineNum;
  894. endId--;
  895. if (curId < 0)
  896. curId = 0;
  897. if (endId >= this._numItems)
  898. endId = this._numItems - 1;
  899. for (; curId <= endId; curId++) {
  900. this.displayData.push(this._calcItemPos(curId));
  901. }
  902. }
  903. this._delRedundantItem();
  904. if (this.displayData.length <= 0 || !this._numItems) { //if none, delete all.
  905. this._lastDisplayData = [];
  906. return;
  907. }
  908. this.firstListId = this.displayData[0].id;
  909. this.displayItemNum = this.displayData.length;
  910. let len: number = this._lastDisplayData.length;
  911. let haveDataChange: boolean = this.displayItemNum != len;
  912. if (haveDataChange) {
  913. // 如果是逐帧渲染,需要排序
  914. if (this.frameByFrameRenderNum > 0) {
  915. this._lastDisplayData.sort((a, b) => { return a - b });
  916. }
  917. // 因List的显示数据是有序的,所以只需要判断数组长度是否相等,以及头、尾两个元素是否相等即可。
  918. haveDataChange = this.firstListId != this._lastDisplayData[0] || this.displayData[this.displayItemNum - 1].id != this._lastDisplayData[len - 1];
  919. }
  920. if (this._forceUpdate || haveDataChange) { //如果是强制更新
  921. if (this.frameByFrameRenderNum > 0) {
  922. // if (this._updateDone) {
  923. // this._lastDisplayData = [];
  924. //逐帧渲染
  925. if (this._numItems > 0) {
  926. if (!this._updateDone) {
  927. this._doneAfterUpdate = true;
  928. } else {
  929. this._updateCounter = 0;
  930. }
  931. this._updateDone = false;
  932. } else {
  933. this._updateCounter = 0;
  934. this._updateDone = true;
  935. }
  936. // }
  937. } else {
  938. //直接渲染
  939. this._lastDisplayData = [];
  940. // cc.log('List Display Data II::', this.displayData);
  941. for (let c = 0; c < this.displayItemNum; c++) {
  942. this._createOrUpdateItem(this.displayData[c]);
  943. }
  944. this._forceUpdate = false;
  945. }
  946. }
  947. this._calcNearestItem();
  948. }
  949. }
  950. //计算可视范围
  951. _calcViewPos() {
  952. let scrollPos: any = this.content.getPosition();
  953. switch (this._alignCalcType) {
  954. case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
  955. this.elasticLeft = scrollPos.x > 0 ? scrollPos.x : 0;
  956. this.viewLeft = (scrollPos.x < 0 ? -scrollPos.x : 0) - this.elasticLeft;
  957. this.viewRight = this.viewLeft + this._thisNodeUt.width;
  958. this.elasticRight = this.viewRight > this._contentUt.width ? Math.abs(this.viewRight - this._contentUt.width) : 0;
  959. this.viewRight += this.elasticRight;
  960. // cc.log(this.elasticLeft, this.elasticRight, this.viewLeft, this.viewRight);
  961. break;
  962. case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
  963. this.elasticRight = scrollPos.x < 0 ? -scrollPos.x : 0;
  964. this.viewRight = (scrollPos.x > 0 ? -scrollPos.x : 0) + this.elasticRight;
  965. this.viewLeft = this.viewRight - this._thisNodeUt.width;
  966. this.elasticLeft = this.viewLeft < -this._contentUt.width ? Math.abs(this.viewLeft + this._contentUt.width) : 0;
  967. this.viewLeft -= this.elasticLeft;
  968. // cc.log(this.elasticLeft, this.elasticRight, this.viewLeft, this.viewRight);
  969. break;
  970. case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
  971. this.elasticTop = scrollPos.y < 0 ? Math.abs(scrollPos.y) : 0;
  972. this.viewTop = (scrollPos.y > 0 ? -scrollPos.y : 0) + this.elasticTop;
  973. this.viewBottom = this.viewTop - this._thisNodeUt.height;
  974. this.elasticBottom = this.viewBottom < -this._contentUt.height ? Math.abs(this.viewBottom + this._contentUt.height) : 0;
  975. this.viewBottom += this.elasticBottom;
  976. // cc.log(this.elasticTop, this.elasticBottom, this.viewTop, this.viewBottom);
  977. break;
  978. case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
  979. this.elasticBottom = scrollPos.y > 0 ? Math.abs(scrollPos.y) : 0;
  980. this.viewBottom = (scrollPos.y < 0 ? -scrollPos.y : 0) - this.elasticBottom;
  981. this.viewTop = this.viewBottom + this._thisNodeUt.height;
  982. this.elasticTop = this.viewTop > this._contentUt.height ? Math.abs(this.viewTop - this._contentUt.height) : 0;
  983. this.viewTop -= this.elasticTop;
  984. // cc.log(this.elasticTop, this.elasticBottom, this.viewTop, this.viewBottom);
  985. break;
  986. }
  987. }
  988. //计算位置 根据id
  989. _calcItemPos(id: number) {
  990. let width: number, height: number, top: number, bottom: number, left: number, right: number, itemX: number, itemY: number;
  991. switch (this._align) {
  992. case Layout.Type.HORIZONTAL:
  993. switch (this._horizontalDir) {
  994. case Layout.HorizontalDirection.LEFT_TO_RIGHT: {
  995. if (this._customSize) {
  996. let fixed: any = this._getFixedSize(id);
  997. left = this._leftGap + ((this._itemSize.width + this._columnGap) * (id - fixed.count)) + (fixed.val + (this._columnGap * fixed.count));
  998. let cs: number = this._customSize[id];
  999. width = (cs > 0 ? cs : this._itemSize.width);
  1000. } else {
  1001. left = this._leftGap + ((this._itemSize.width + this._columnGap) * id);
  1002. width = this._itemSize.width;
  1003. }
  1004. if (this.lackCenter) {
  1005. left -= this._leftGap;
  1006. let offset: number = (this._contentUt.width / 2) - (this._allItemSizeNoEdge / 2);
  1007. left += offset;
  1008. }
  1009. right = left + width;
  1010. return {
  1011. id: id,
  1012. left: left,
  1013. right: right,
  1014. x: left + (this._itemTmpUt.anchorX * width),
  1015. y: this._itemTmp.y,
  1016. };
  1017. }
  1018. case Layout.HorizontalDirection.RIGHT_TO_LEFT: {
  1019. if (this._customSize) {
  1020. let fixed: any = this._getFixedSize(id);
  1021. right = -this._rightGap - ((this._itemSize.width + this._columnGap) * (id - fixed.count)) - (fixed.val + (this._columnGap * fixed.count));
  1022. let cs: number = this._customSize[id];
  1023. width = (cs > 0 ? cs : this._itemSize.width);
  1024. } else {
  1025. right = -this._rightGap - ((this._itemSize.width + this._columnGap) * id);
  1026. width = this._itemSize.width;
  1027. }
  1028. if (this.lackCenter) {
  1029. right += this._rightGap;
  1030. let offset: number = (this._contentUt.width / 2) - (this._allItemSizeNoEdge / 2);
  1031. right -= offset;
  1032. }
  1033. left = right - width;
  1034. return {
  1035. id: id,
  1036. right: right,
  1037. left: left,
  1038. x: left + (this._itemTmpUt.anchorX * width),
  1039. y: this._itemTmp.y,
  1040. };
  1041. }
  1042. }
  1043. break;
  1044. case Layout.Type.VERTICAL: {
  1045. switch (this._verticalDir) {
  1046. case Layout.VerticalDirection.TOP_TO_BOTTOM: {
  1047. if (this._customSize) {
  1048. let fixed: any = this._getFixedSize(id);
  1049. top = -this._topGap - ((this._itemSize.height + this._lineGap) * (id - fixed.count)) - (fixed.val + (this._lineGap * fixed.count));
  1050. let cs: number = this._customSize[id];
  1051. height = (cs > 0 ? cs : this._itemSize.height);
  1052. } else {
  1053. top = -this._topGap - ((this._itemSize.height + this._lineGap) * id);
  1054. height = this._itemSize.height;
  1055. }
  1056. if (this.lackCenter) {
  1057. top += this._topGap;
  1058. let offset: number = (this._contentUt.height / 2) - (this._allItemSizeNoEdge / 2);
  1059. top -= offset;
  1060. }
  1061. bottom = top - height;
  1062. return {
  1063. id: id,
  1064. top: top,
  1065. bottom: bottom,
  1066. x: this._itemTmp.x,
  1067. y: bottom + (this._itemTmpUt.anchorY * height),
  1068. };
  1069. }
  1070. case Layout.VerticalDirection.BOTTOM_TO_TOP: {
  1071. if (this._customSize) {
  1072. let fixed: any = this._getFixedSize(id);
  1073. bottom = this._bottomGap + ((this._itemSize.height + this._lineGap) * (id - fixed.count)) + (fixed.val + (this._lineGap * fixed.count));
  1074. let cs: number = this._customSize[id];
  1075. height = (cs > 0 ? cs : this._itemSize.height);
  1076. } else {
  1077. bottom = this._bottomGap + ((this._itemSize.height + this._lineGap) * id);
  1078. height = this._itemSize.height;
  1079. }
  1080. if (this.lackCenter) {
  1081. bottom -= this._bottomGap;
  1082. let offset: number = (this._contentUt.height / 2) - (this._allItemSizeNoEdge / 2);
  1083. bottom += offset;
  1084. }
  1085. top = bottom + height;
  1086. return {
  1087. id: id,
  1088. top: top,
  1089. bottom: bottom,
  1090. x: this._itemTmp.x,
  1091. y: bottom + (this._itemTmpUt.anchorY * height),
  1092. };
  1093. break;
  1094. }
  1095. }
  1096. }
  1097. case Layout.Type.GRID: {
  1098. let colLine: number = Math.floor(id / this._colLineNum);
  1099. switch (this._startAxis) {
  1100. case Layout.AxisDirection.HORIZONTAL: {
  1101. switch (this._verticalDir) {
  1102. case Layout.VerticalDirection.TOP_TO_BOTTOM: {
  1103. top = -this._topGap - ((this._itemSize.height + this._lineGap) * colLine);
  1104. bottom = top - this._itemSize.height;
  1105. itemY = bottom + (this._itemTmpUt.anchorY * this._itemSize.height);
  1106. break;
  1107. }
  1108. case Layout.VerticalDirection.BOTTOM_TO_TOP: {
  1109. bottom = this._bottomGap + ((this._itemSize.height + this._lineGap) * colLine);
  1110. top = bottom + this._itemSize.height;
  1111. itemY = bottom + (this._itemTmpUt.anchorY * this._itemSize.height);
  1112. break;
  1113. }
  1114. }
  1115. itemX = this._leftGap + ((id % this._colLineNum) * (this._itemSize.width + this._columnGap));
  1116. switch (this._horizontalDir) {
  1117. case Layout.HorizontalDirection.LEFT_TO_RIGHT: {
  1118. itemX += (this._itemTmpUt.anchorX * this._itemSize.width);
  1119. itemX -= (this._contentUt.anchorX * this._contentUt.width);
  1120. break;
  1121. }
  1122. case Layout.HorizontalDirection.RIGHT_TO_LEFT: {
  1123. itemX += ((1 - this._itemTmpUt.anchorX) * this._itemSize.width);
  1124. itemX -= ((1 - this._contentUt.anchorX) * this._contentUt.width);
  1125. itemX *= -1;
  1126. break;
  1127. }
  1128. }
  1129. return {
  1130. id: id,
  1131. top: top,
  1132. bottom: bottom,
  1133. x: itemX,
  1134. y: itemY,
  1135. };
  1136. }
  1137. case Layout.AxisDirection.VERTICAL: {
  1138. switch (this._horizontalDir) {
  1139. case Layout.HorizontalDirection.LEFT_TO_RIGHT: {
  1140. left = this._leftGap + ((this._itemSize.width + this._columnGap) * colLine);
  1141. right = left + this._itemSize.width;
  1142. itemX = left + (this._itemTmpUt.anchorX * this._itemSize.width);
  1143. itemX -= (this._contentUt.anchorX * this._contentUt.width);
  1144. break;
  1145. }
  1146. case Layout.HorizontalDirection.RIGHT_TO_LEFT: {
  1147. right = -this._rightGap - ((this._itemSize.width + this._columnGap) * colLine);
  1148. left = right - this._itemSize.width;
  1149. itemX = left + (this._itemTmpUt.anchorX * this._itemSize.width);
  1150. itemX += ((1 - this._contentUt.anchorX) * this._contentUt.width);
  1151. break;
  1152. }
  1153. }
  1154. itemY = -this._topGap - ((id % this._colLineNum) * (this._itemSize.height + this._lineGap));
  1155. switch (this._verticalDir) {
  1156. case Layout.VerticalDirection.TOP_TO_BOTTOM: {
  1157. itemY -= ((1 - this._itemTmpUt.anchorY) * this._itemSize.height);
  1158. itemY += ((1 - this._contentUt.anchorY) * this._contentUt.height);
  1159. break;
  1160. }
  1161. case Layout.VerticalDirection.BOTTOM_TO_TOP: {
  1162. itemY -= ((this._itemTmpUt.anchorY) * this._itemSize.height);
  1163. itemY += (this._contentUt.anchorY * this._contentUt.height);
  1164. itemY *= -1;
  1165. break;
  1166. }
  1167. }
  1168. return {
  1169. id: id,
  1170. left: left,
  1171. right: right,
  1172. x: itemX,
  1173. y: itemY,
  1174. };
  1175. }
  1176. }
  1177. break;
  1178. }
  1179. }
  1180. }
  1181. //计算已存在的Item的位置
  1182. _calcExistItemPos(id: number) {
  1183. let item: any = this.getItemByListId(id);
  1184. if (!item)
  1185. return null;
  1186. let ut: UITransform = item.getComponent(UITransform);
  1187. let pos: Vec3 = item.getPosition();
  1188. let data: any = {
  1189. id: id,
  1190. x: pos.x,
  1191. y: pos.y,
  1192. }
  1193. if (this._sizeType) {
  1194. data.top = pos.y + (ut.height * (1 - ut.anchorY));
  1195. data.bottom = pos.y - (ut.height * ut.anchorY);
  1196. } else {
  1197. data.left = pos.x - (ut.width * ut.anchorX);
  1198. data.right = pos.x + (ut.width * (1 - ut.anchorX));
  1199. }
  1200. return data;
  1201. }
  1202. //获取Item位置
  1203. getItemPos(id: number) {
  1204. if (this._virtual)
  1205. return this._calcItemPos(id);
  1206. else {
  1207. if (this.frameByFrameRenderNum)
  1208. return this._calcItemPos(id);
  1209. else
  1210. return this._calcExistItemPos(id);
  1211. }
  1212. }
  1213. //获取固定尺寸
  1214. _getFixedSize(listId: number) {
  1215. if (!this._customSize)
  1216. return null;
  1217. if (listId == null)
  1218. listId = this._numItems;
  1219. let fixed: number = 0;
  1220. let count: number = 0;
  1221. for (let id in this._customSize) {
  1222. if (parseInt(id) < listId) {
  1223. fixed += this._customSize[id];
  1224. count++;
  1225. }
  1226. }
  1227. return {
  1228. val: fixed,
  1229. count: count,
  1230. }
  1231. }
  1232. //滚动结束时..
  1233. _onScrollBegan() {
  1234. this._beganPos = this._sizeType ? this.viewTop : this.viewLeft;
  1235. }
  1236. //滚动结束时..
  1237. _onScrollEnded() {
  1238. let t: any = this;
  1239. t._curScrollIsTouch = false;
  1240. if (t.scrollToListId != null) {
  1241. let item: any = t.getItemByListId(t.scrollToListId);
  1242. t.scrollToListId = null;
  1243. if (item) {
  1244. tween(item)
  1245. .to(.1, { scale: 1.06 })
  1246. .to(.1, { scale: 1 })
  1247. .start();
  1248. }
  1249. }
  1250. t._onScrolling();
  1251. if (t._slideMode == SlideType.ADHERING &&
  1252. !t.adhering
  1253. ) {
  1254. //cc.log(t.adhering, t._scrollView.isAutoScrolling(), t._scrollView.isScrolling());
  1255. t.adhere();
  1256. } else if (t._slideMode == SlideType.PAGE) {
  1257. if (t._beganPos != null && t._curScrollIsTouch) {
  1258. this._pageAdhere();
  1259. } else {
  1260. t.adhere();
  1261. }
  1262. }
  1263. }
  1264. // 触摸时
  1265. _onTouchStart(ev, captureListeners) {
  1266. if (this._scrollView['_hasNestedViewGroup'](ev, captureListeners))
  1267. return;
  1268. this._curScrollIsTouch = true;
  1269. let isMe = ev.eventPhase === Event.AT_TARGET && ev.target === this.node;
  1270. if (!isMe) {
  1271. let itemNode: any = ev.target;
  1272. while (itemNode._listId == null && itemNode.parent)
  1273. itemNode = itemNode.parent;
  1274. this._scrollItem = itemNode._listId != null ? itemNode : ev.target;
  1275. }
  1276. }
  1277. //触摸抬起时..
  1278. _onTouchUp() {
  1279. let t: any = this;
  1280. t._scrollPos = null;
  1281. if (t._slideMode == SlideType.ADHERING) {
  1282. if (this.adhering)
  1283. this._adheringBarrier = true;
  1284. t.adhere();
  1285. } else if (t._slideMode == SlideType.PAGE) {
  1286. if (t._beganPos != null) {
  1287. this._pageAdhere();
  1288. } else {
  1289. t.adhere();
  1290. }
  1291. }
  1292. this._scrollItem = null;
  1293. }
  1294. _onTouchCancelled(ev, captureListeners) {
  1295. let t = this;
  1296. if (t._scrollView['_hasNestedViewGroup'](ev, captureListeners) || ev.simulate)
  1297. return;
  1298. t._scrollPos = null;
  1299. if (t._slideMode == SlideType.ADHERING) {
  1300. if (t.adhering)
  1301. t._adheringBarrier = true;
  1302. t.adhere();
  1303. } else if (t._slideMode == SlideType.PAGE) {
  1304. if (t._beganPos != null) {
  1305. t._pageAdhere();
  1306. } else {
  1307. t.adhere();
  1308. }
  1309. }
  1310. this._scrollItem = null;
  1311. }
  1312. //当尺寸改变
  1313. _onSizeChanged() {
  1314. if (this.checkInited(false))
  1315. this._onScrolling();
  1316. }
  1317. //当Item自适应
  1318. _onItemAdaptive(item: any) {
  1319. let ut: UITransform = item.getComponent(UITransform);
  1320. // if (this.checkInited(false)) {
  1321. if (
  1322. (!this._sizeType && ut.width != this._itemSize.width)
  1323. || (this._sizeType && ut.height != this._itemSize.height)
  1324. ) {
  1325. if (!this._customSize)
  1326. this._customSize = {};
  1327. let val = this._sizeType ? ut.height : ut.width;
  1328. if (this._customSize[item._listId] != val) {
  1329. this._customSize[item._listId] = val;
  1330. this._resizeContent();
  1331. // this.content.children.forEach((child: Node) => {
  1332. // this._updateItemPos(child);
  1333. // });
  1334. this.updateAll();
  1335. // 如果当前正在运行 scrollTo,肯定会不准确,在这里做修正
  1336. if (this._scrollToListId != null) {
  1337. this._scrollPos = null;
  1338. this.unschedule(this._scrollToSo);
  1339. this.scrollTo(this._scrollToListId, Math.max(0, this._scrollToEndTime - ((new Date()).getTime() / 1000)));
  1340. }
  1341. }
  1342. }
  1343. // }
  1344. }
  1345. //PAGE粘附
  1346. _pageAdhere() {
  1347. let t = this;
  1348. if (!t.cyclic && (t.elasticTop > 0 || t.elasticRight > 0 || t.elasticBottom > 0 || t.elasticLeft > 0))
  1349. return;
  1350. let curPos = t._sizeType ? t.viewTop : t.viewLeft;
  1351. let dis = (t._sizeType ? t._thisNodeUt.height : t._thisNodeUt.width) * t.pageDistance;
  1352. let canSkip = Math.abs(t._beganPos - curPos) > dis;
  1353. if (canSkip) {
  1354. let timeInSecond = .5;
  1355. switch (t._alignCalcType) {
  1356. case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
  1357. case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
  1358. if (t._beganPos > curPos) {
  1359. t.prePage(timeInSecond);
  1360. // cc.log('_pageAdhere PPPPPPPPPPPPPPP');
  1361. } else {
  1362. t.nextPage(timeInSecond);
  1363. // cc.log('_pageAdhere NNNNNNNNNNNNNNN');
  1364. }
  1365. break;
  1366. case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
  1367. case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
  1368. if (t._beganPos < curPos) {
  1369. t.prePage(timeInSecond);
  1370. } else {
  1371. t.nextPage(timeInSecond);
  1372. }
  1373. break;
  1374. }
  1375. } else if (t.elasticTop <= 0 && t.elasticRight <= 0 && t.elasticBottom <= 0 && t.elasticLeft <= 0) {
  1376. t.adhere();
  1377. }
  1378. t._beganPos = null;
  1379. }
  1380. //粘附
  1381. adhere() {
  1382. let t: any = this;
  1383. if (!t.checkInited())
  1384. return;
  1385. if (t.elasticTop > 0 || t.elasticRight > 0 || t.elasticBottom > 0 || t.elasticLeft > 0)
  1386. return;
  1387. t.adhering = true;
  1388. t._calcNearestItem();
  1389. let offset: number = (t._sizeType ? t._topGap : t._leftGap) / (t._sizeType ? t._thisNodeUt.height : t._thisNodeUt.width);
  1390. let timeInSecond: number = .7;
  1391. t.scrollTo(t.nearestListId, timeInSecond, offset);
  1392. }
  1393. //Update..
  1394. update() {
  1395. if (this.frameByFrameRenderNum <= 0 || this._updateDone)
  1396. return;
  1397. // cc.log(this.displayData.length, this._updateCounter, this.displayData[this._updateCounter]);
  1398. if (this._virtual) {
  1399. let len: number = (this._updateCounter + this.frameByFrameRenderNum) > this.displayItemNum ? this.displayItemNum : (this._updateCounter + this.frameByFrameRenderNum);
  1400. for (let n: number = this._updateCounter; n < len; n++) {
  1401. let data: any = this.displayData[n];
  1402. if (data) {
  1403. this._createOrUpdateItem(data);
  1404. }
  1405. }
  1406. if (this._updateCounter >= this.displayItemNum - 1) { //最后一个
  1407. if (this._doneAfterUpdate) {
  1408. this._updateCounter = 0;
  1409. this._updateDone = false;
  1410. // if (!this._scrollView.isScrolling())
  1411. this._doneAfterUpdate = false;
  1412. } else {
  1413. this._updateDone = true;
  1414. this._delRedundantItem();
  1415. this._forceUpdate = false;
  1416. this._calcNearestItem();
  1417. if (this.slideMode == SlideType.PAGE)
  1418. this.curPageNum = this.nearestListId;
  1419. }
  1420. } else {
  1421. this._updateCounter += this.frameByFrameRenderNum;
  1422. }
  1423. } else {
  1424. if (this._updateCounter < this._numItems) {
  1425. let len: number = (this._updateCounter + this.frameByFrameRenderNum) > this._numItems ? this._numItems : (this._updateCounter + this.frameByFrameRenderNum);
  1426. for (let n: number = this._updateCounter; n < len; n++) {
  1427. this._createOrUpdateItem2(n);
  1428. }
  1429. this._updateCounter += this.frameByFrameRenderNum;
  1430. } else {
  1431. this._updateDone = true;
  1432. this._calcNearestItem();
  1433. if (this.slideMode == SlideType.PAGE)
  1434. this.curPageNum = this.nearestListId;
  1435. }
  1436. }
  1437. }
  1438. /**
  1439. * 创建或更新Item(虚拟列表用)
  1440. * @param {Object} data 数据
  1441. */
  1442. _createOrUpdateItem(data: any) {
  1443. let item: any = this.getItemByListId(data.id);
  1444. if (!item) { //如果不存在
  1445. let canGet: boolean = this._pool.size() > 0;
  1446. if (canGet) {
  1447. item = this._pool.get();
  1448. // cc.log('从池中取出:: 旧id =', item['_listId'], ',新id =', data.id, item);
  1449. } else {
  1450. item = instantiate(this._itemTmp);
  1451. // cc.log('新建::', data.id, item);
  1452. }
  1453. if (!canGet || !isValid(item)) {
  1454. item = instantiate(this._itemTmp);
  1455. canGet = false;
  1456. }
  1457. if (item._listId != data.id) {
  1458. item._listId = data.id;
  1459. let ut: UITransform = item.getComponent(UITransform);
  1460. ut.setContentSize(this._itemSize);
  1461. }
  1462. item.setPosition(new Vec3(data.x, data.y, 0));
  1463. this._resetItemSize(item);
  1464. this.content.addChild(item);
  1465. if (canGet && this._needUpdateWidget) {
  1466. let widget: Widget = item.getComponent(Widget);
  1467. if (widget)
  1468. widget.updateAlignment();
  1469. }
  1470. item.setSiblingIndex(this.content.children.length - 1);
  1471. let listItem: ListItem = item.getComponent(ListItem);
  1472. item['listItem'] = listItem;
  1473. if (listItem) {
  1474. listItem.listId = data.id;
  1475. listItem.list = this;
  1476. listItem._registerEvent();
  1477. }
  1478. if (this.renderEvent) {
  1479. EventHandler.emitEvents([this.renderEvent], item, data.id % this._actualNumItems);
  1480. }
  1481. } else if (this._forceUpdate && this.renderEvent) { //强制更新
  1482. item.setPosition(new Vec3(data.x, data.y, 0));
  1483. this._resetItemSize(item);
  1484. // cc.log('ADD::', data.id, item);
  1485. if (this.renderEvent) {
  1486. EventHandler.emitEvents([this.renderEvent], item, data.id % this._actualNumItems);
  1487. }
  1488. }
  1489. this._resetItemSize(item);
  1490. this._updateListItem(item['listItem']);
  1491. if (this._lastDisplayData.indexOf(data.id) < 0) {
  1492. this._lastDisplayData.push(data.id);
  1493. }
  1494. }
  1495. //创建或更新Item(非虚拟列表用)
  1496. _createOrUpdateItem2(listId: number) {
  1497. let item: any = this.content.children[listId];
  1498. let listItem: ListItem;
  1499. if (!item) { //如果不存在
  1500. item = instantiate(this._itemTmp);
  1501. item._listId = listId;
  1502. this.content.addChild(item);
  1503. listItem = item.getComponent(ListItem);
  1504. item['listItem'] = listItem;
  1505. if (listItem) {
  1506. listItem.listId = listId;
  1507. listItem.list = this;
  1508. listItem._registerEvent();
  1509. }
  1510. if (this.renderEvent) {
  1511. EventHandler.emitEvents([this.renderEvent], item, listId % this._actualNumItems);
  1512. }
  1513. } else if (this._forceUpdate && this.renderEvent) { //强制更新
  1514. item._listId = listId;
  1515. if (listItem)
  1516. listItem.listId = listId;
  1517. if (this.renderEvent) {
  1518. EventHandler.emitEvents([this.renderEvent], item, listId % this._actualNumItems);
  1519. }
  1520. }
  1521. this._updateListItem(listItem);
  1522. if (this._lastDisplayData.indexOf(listId) < 0) {
  1523. this._lastDisplayData.push(listId);
  1524. }
  1525. }
  1526. _updateListItem(listItem: ListItem) {
  1527. if (!listItem)
  1528. return;
  1529. if (this.selectedMode > SelectedType.NONE) {
  1530. let item: any = listItem.node;
  1531. switch (this.selectedMode) {
  1532. case SelectedType.SINGLE:
  1533. listItem.selected = this.selectedId == item._listId;
  1534. break;
  1535. case SelectedType.MULT:
  1536. listItem.selected = this.multSelected.indexOf(item._listId) >= 0;
  1537. break;
  1538. }
  1539. }
  1540. }
  1541. //仅虚拟列表用
  1542. _resetItemSize(item: any) {
  1543. return;
  1544. let size: number;
  1545. let ut: UITransform = item.getComponent(UITransform);
  1546. if (this._customSize && this._customSize[item._listId]) {
  1547. size = this._customSize[item._listId];
  1548. } else {
  1549. if (this._colLineNum > 1)
  1550. ut.setContentSize(this._itemSize);
  1551. else
  1552. size = this._sizeType ? this._itemSize.height : this._itemSize.width;
  1553. }
  1554. if (size) {
  1555. if (this._sizeType)
  1556. ut.height = size;
  1557. else
  1558. ut.width = size;
  1559. }
  1560. }
  1561. /**
  1562. * 更新Item位置
  1563. * @param {Number||Node} listIdOrItem
  1564. */
  1565. _updateItemPos(listIdOrItem: any) {
  1566. let item: any = isNaN(listIdOrItem) ? listIdOrItem : this.getItemByListId(listIdOrItem);
  1567. let pos: any = this.getItemPos(item._listId);
  1568. item.setPosition(pos.x, pos.y);
  1569. }
  1570. /**
  1571. * 设置多选
  1572. * @param {Array} args 可以是单个listId,也可是个listId数组
  1573. * @param {Boolean} bool 值,如果为null的话,则直接用args覆盖
  1574. */
  1575. setMultSelected(args: any, bool: boolean) {
  1576. let t: any = this;
  1577. if (!t.checkInited())
  1578. return;
  1579. if (!Array.isArray(args)) {
  1580. args = [args];
  1581. }
  1582. if (bool == null) {
  1583. t.multSelected = args;
  1584. } else {
  1585. let listId: number, sub: number;
  1586. if (bool) {
  1587. for (let n: number = args.length - 1; n >= 0; n--) {
  1588. listId = args[n];
  1589. sub = t.multSelected.indexOf(listId);
  1590. if (sub < 0) {
  1591. t.multSelected.push(listId);
  1592. }
  1593. }
  1594. } else {
  1595. for (let n: number = args.length - 1; n >= 0; n--) {
  1596. listId = args[n];
  1597. sub = t.multSelected.indexOf(listId);
  1598. if (sub >= 0) {
  1599. t.multSelected.splice(sub, 1);
  1600. }
  1601. }
  1602. }
  1603. }
  1604. t._forceUpdate = true;
  1605. t._onScrolling();
  1606. }
  1607. /**
  1608. * 获取多选数据
  1609. * @returns
  1610. */
  1611. getMultSelected() {
  1612. return this.multSelected;
  1613. }
  1614. /**
  1615. * 多选是否有选择
  1616. * @param {number} listId 索引
  1617. * @returns
  1618. */
  1619. hasMultSelected(listId: number) {
  1620. return this.multSelected && this.multSelected.indexOf(listId) >= 0;
  1621. }
  1622. /**
  1623. * 更新指定的Item
  1624. * @param {Array} args 单个listId,或者数组
  1625. * @returns
  1626. */
  1627. updateItem(args: any) {
  1628. if (!this.checkInited())
  1629. return;
  1630. if (!Array.isArray(args)) {
  1631. args = [args];
  1632. }
  1633. for (let n: number = 0, len: number = args.length; n < len; n++) {
  1634. let listId: number = args[n];
  1635. let item: any = this.getItemByListId(listId);
  1636. if (item)
  1637. EventHandler.emitEvents([this.renderEvent], item, listId % this._actualNumItems);
  1638. }
  1639. }
  1640. /**
  1641. * 更新全部
  1642. */
  1643. updateAll() {
  1644. if (!this.checkInited())
  1645. return;
  1646. this.numItems = this.numItems;
  1647. }
  1648. /**
  1649. * 根据ListID获取Item
  1650. * @param {Number} listId
  1651. * @returns
  1652. */
  1653. getItemByListId(listId: number) {
  1654. if (this.content) {
  1655. for (let n: number = this.content.children.length - 1; n >= 0; n--) {
  1656. let item: any = this.content.children[n];
  1657. if (item._listId == listId)
  1658. return item;
  1659. }
  1660. }
  1661. }
  1662. /**
  1663. * 获取在显示区域外的Item
  1664. * @returns
  1665. */
  1666. _getOutsideItem() {
  1667. let item: any;
  1668. let result: any[] = [];
  1669. for (let n: number = this.content.children.length - 1; n >= 0; n--) {
  1670. item = this.content.children[n];
  1671. if (!this.displayData.find(d => d.id == item._listId)) {
  1672. result.push(item);
  1673. }
  1674. }
  1675. return result;
  1676. }
  1677. //删除显示区域以外的Item
  1678. _delRedundantItem() {
  1679. if (this._virtual) {
  1680. let arr: any[] = this._getOutsideItem();
  1681. for (let n: number = arr.length - 1; n >= 0; n--) {
  1682. let item: any = arr[n];
  1683. if (this._scrollItem && item._listId == this._scrollItem._listId)
  1684. continue;
  1685. item.isCached = true;
  1686. this._pool.put(item);
  1687. for (let m: number = this._lastDisplayData.length - 1; m >= 0; m--) {
  1688. if (this._lastDisplayData[m] == item._listId) {
  1689. this._lastDisplayData.splice(m, 1);
  1690. break;
  1691. }
  1692. }
  1693. }
  1694. // cc.log('存入::', str, ' pool.length =', this._pool.length);
  1695. } else {
  1696. while (this.content.children.length > this._numItems) {
  1697. this._delSingleItem(this.content.children[this.content.children.length - 1]);
  1698. }
  1699. }
  1700. }
  1701. //删除单个Item
  1702. _delSingleItem(item: any) {
  1703. // cc.log('DEL::', item['_listId'], item);
  1704. item.removeFromParent();
  1705. if (item.destroy)
  1706. item.destroy();
  1707. item = null;
  1708. }
  1709. /**
  1710. * 动效删除Item(此方法只适用于虚拟列表,即_virtual=true)
  1711. * 一定要在回调函数里重新设置新的numItems进行刷新,毕竟本List是靠数据驱动的。
  1712. */
  1713. aniDelItem(listId: number, callFunc: Function, aniType: number) {
  1714. let t: any = this;
  1715. if (!t.checkInited() || t.cyclic || !t._virtual)
  1716. return console.error('This function is not allowed to be called!');
  1717. if (!callFunc)
  1718. return console.error('CallFunc are not allowed to be NULL, You need to delete the corresponding index in the data array in the CallFunc!');
  1719. if (t._aniDelRuning)
  1720. return console.warn('Please wait for the current deletion to finish!');
  1721. let item: any = t.getItemByListId(listId);
  1722. let listItem: ListItem;
  1723. if (!item) {
  1724. callFunc(listId);
  1725. return;
  1726. } else {
  1727. listItem = item.getComponent(ListItem);
  1728. }
  1729. t._aniDelRuning = true;
  1730. t._aniDelCB = callFunc;
  1731. t._aniDelItem = item;
  1732. t._aniDelBeforePos = item.position;
  1733. t._aniDelBeforeScale = item.scale;
  1734. let curLastId: number = t.displayData[t.displayData.length - 1].id;
  1735. let resetSelectedId: boolean = listItem.selected;
  1736. listItem.showAni(aniType, () => {
  1737. //判断有没有下一个,如果有的话,创建粗来
  1738. let newId: number;
  1739. if (curLastId < t._numItems - 2) {
  1740. newId = curLastId + 1;
  1741. }
  1742. if (newId != null) {
  1743. let newData: any = t._calcItemPos(newId);
  1744. t.displayData.push(newData);
  1745. if (t._virtual)
  1746. t._createOrUpdateItem(newData);
  1747. else
  1748. t._createOrUpdateItem2(newId);
  1749. } else
  1750. t._numItems--;
  1751. if (t.selectedMode == SelectedType.SINGLE) {
  1752. if (resetSelectedId) {
  1753. t._selectedId = -1;
  1754. } else if (t._selectedId - 1 >= 0) {
  1755. t._selectedId--;
  1756. }
  1757. } else if (t.selectedMode == SelectedType.MULT && t.multSelected.length) {
  1758. let sub: number = t.multSelected.indexOf(listId);
  1759. if (sub >= 0) {
  1760. t.multSelected.splice(sub, 1);
  1761. }
  1762. //多选的数据,在其后的全部减一
  1763. for (let n: number = t.multSelected.length - 1; n >= 0; n--) {
  1764. let id: number = t.multSelected[n];
  1765. if (id >= listId)
  1766. t.multSelected[n]--;
  1767. }
  1768. }
  1769. if (t._customSize) {
  1770. if (t._customSize[listId])
  1771. delete t._customSize[listId];
  1772. let newCustomSize: any = {};
  1773. let size: number;
  1774. for (let id in t._customSize) {
  1775. size = t._customSize[id];
  1776. let idNumber: number = parseInt(id);
  1777. newCustomSize[idNumber - (idNumber >= listId ? 1 : 0)] = size;
  1778. }
  1779. t._customSize = newCustomSize;
  1780. }
  1781. //后面的Item向前怼的动效
  1782. let sec: number = .2333;
  1783. let twe: Tween<Node>, haveCB: boolean;
  1784. for (let n: number = newId != null ? newId : curLastId; n >= listId + 1; n--) {
  1785. item = t.getItemByListId(n);
  1786. if (item) {
  1787. let posData: any = t._calcItemPos(n - 1);
  1788. twe = tween(item)
  1789. .to(sec, { position: new Vec3(posData.x, posData.y, 0) });
  1790. if (n <= listId + 1) {
  1791. haveCB = true;
  1792. twe.call(() => {
  1793. t._aniDelRuning = false;
  1794. callFunc(listId);
  1795. delete t._aniDelCB;
  1796. });
  1797. }
  1798. twe.start();
  1799. }
  1800. }
  1801. if (!haveCB) {
  1802. t._aniDelRuning = false;
  1803. callFunc(listId);
  1804. t._aniDelCB = null;
  1805. }
  1806. }, true);
  1807. }
  1808. /**
  1809. * 滚动到..
  1810. * @param {Number} listId 索引(如果<0,则滚到首个Item位置,如果>=_numItems,则滚到最末Item位置)
  1811. * @param {Number} timeInSecond 时间
  1812. * @param {Number} offset 索引目标位置偏移,0-1
  1813. * @param {Boolean} overStress 滚动后是否强调该Item(这只是个实验功能)
  1814. */
  1815. scrollTo(listId: number, timeInSecond: number = .5, offset: number = null, overStress: boolean = false) {
  1816. let t = this;
  1817. if (!t.checkInited(false))
  1818. return;
  1819. // t._scrollView.stopAutoScroll();
  1820. if (timeInSecond == null) //默认0.5
  1821. timeInSecond = .5;
  1822. else if (timeInSecond < 0)
  1823. timeInSecond = 0;
  1824. if (listId < 0)
  1825. listId = 0;
  1826. else if (listId >= t._numItems)
  1827. listId = t._numItems - 1;
  1828. // 以防设置了numItems之后layout的尺寸还未更新
  1829. if (!t._virtual && t._layout && t._layout.enabled)
  1830. t._layout.updateLayout();
  1831. let pos = t.getItemPos(listId);
  1832. if (!pos) {
  1833. return DEV && console.error('pos is null', listId);
  1834. }
  1835. let targetX: number, targetY: number;
  1836. switch (t._alignCalcType) {
  1837. case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
  1838. targetX = pos.left;
  1839. if (offset != null)
  1840. targetX -= t._thisNodeUt.width * offset;
  1841. else
  1842. targetX -= t._leftGap;
  1843. pos = new Vec3(targetX, 0, 0);
  1844. break;
  1845. case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
  1846. targetX = pos.right - t._thisNodeUt.width;
  1847. if (offset != null)
  1848. targetX += t._thisNodeUt.width * offset;
  1849. else
  1850. targetX += t._rightGap;
  1851. pos = new Vec3(targetX + t._contentUt.width, 0, 0);
  1852. break;
  1853. case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
  1854. targetY = pos.top;
  1855. if (offset != null)
  1856. targetY += t._thisNodeUt.height * offset;
  1857. else
  1858. targetY += t._topGap;
  1859. pos = new Vec3(0, -targetY, 0);
  1860. break;
  1861. case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
  1862. targetY = pos.bottom + t._thisNodeUt.height;
  1863. if (offset != null)
  1864. targetY -= t._thisNodeUt.height * offset;
  1865. else
  1866. targetY -= t._bottomGap;
  1867. pos = new Vec3(0, -targetY + t._contentUt.height, 0);
  1868. break;
  1869. }
  1870. let viewPos: any = t.content.getPosition();
  1871. viewPos = Math.abs(t._sizeType ? viewPos.y : viewPos.x);
  1872. let comparePos = t._sizeType ? pos.y : pos.x;
  1873. let runScroll = Math.abs((t._scrollPos != null ? t._scrollPos : viewPos) - comparePos) > .5;
  1874. // cc.log(runScroll, t._scrollPos, viewPos, comparePos)
  1875. // t._scrollView.stopAutoScroll();
  1876. if (runScroll) {
  1877. t._scrollView.scrollToOffset(pos, timeInSecond);
  1878. t._scrollToListId = listId;
  1879. t._scrollToEndTime = ((new Date()).getTime() / 1000) + timeInSecond;
  1880. // cc.log(listId, t.content.width, t.content.getPosition(), pos);
  1881. t._scrollToSo = t.scheduleOnce(() => {
  1882. if (!t._adheringBarrier) {
  1883. t.adhering = t._adheringBarrier = false;
  1884. }
  1885. t._scrollPos =
  1886. t._scrollToListId =
  1887. t._scrollToEndTime =
  1888. t._scrollToSo =
  1889. null;
  1890. //cc.log('2222222222', t._adheringBarrier)
  1891. if (overStress) {
  1892. // t.scrollToListId = listId;
  1893. let item = t.getItemByListId(listId);
  1894. if (item) {
  1895. tween(item)
  1896. .to(.1, { scale: 1.05 })
  1897. .to(.1, { scale: 1 })
  1898. .start();
  1899. }
  1900. }
  1901. }, timeInSecond + .1);
  1902. if (timeInSecond <= 0) {
  1903. t._onScrolling();
  1904. }
  1905. }
  1906. }
  1907. /**
  1908. * 计算当前滚动窗最近的Item
  1909. */
  1910. _calcNearestItem() {
  1911. let t: any = this;
  1912. t.nearestListId = null;
  1913. let data: any, center: number;
  1914. if (t._virtual)
  1915. t._calcViewPos();
  1916. let vTop: number, vRight: number, vBottom: number, vLeft: number;
  1917. vTop = t.viewTop;
  1918. vRight = t.viewRight;
  1919. vBottom = t.viewBottom;
  1920. vLeft = t.viewLeft;
  1921. let breakFor: boolean = false;
  1922. for (let n = 0; n < t.content.children.length && !breakFor; n += t._colLineNum) {
  1923. data = t._virtual ? t.displayData[n] : t._calcExistItemPos(n);
  1924. if (data) {
  1925. center = t._sizeType ? ((data.top + data.bottom) / 2) : (center = (data.left + data.right) / 2);
  1926. switch (t._alignCalcType) {
  1927. case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
  1928. if (data.right >= vLeft) {
  1929. t.nearestListId = data.id;
  1930. if (vLeft > center)
  1931. t.nearestListId += t._colLineNum;
  1932. breakFor = true;
  1933. }
  1934. break;
  1935. case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
  1936. if (data.left <= vRight) {
  1937. t.nearestListId = data.id;
  1938. if (vRight < center)
  1939. t.nearestListId += t._colLineNum;
  1940. breakFor = true;
  1941. }
  1942. break;
  1943. case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
  1944. if (data.bottom <= vTop) {
  1945. t.nearestListId = data.id;
  1946. if (vTop < center)
  1947. t.nearestListId += t._colLineNum;
  1948. breakFor = true;
  1949. }
  1950. break;
  1951. case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
  1952. if (data.top >= vBottom) {
  1953. t.nearestListId = data.id;
  1954. if (vBottom > center)
  1955. t.nearestListId += t._colLineNum;
  1956. breakFor = true;
  1957. }
  1958. break;
  1959. }
  1960. }
  1961. }
  1962. //判断最后一个Item。。。(哎,这些判断真心恶心,判断了前面的还要判断最后一个。。。一开始呢,就只有一个布局(单列布局),那时候代码才三百行,后来就想着完善啊,艹..这坑真深,现在这行数都一千五了= =||)
  1963. data = t._virtual ? t.displayData[t.displayItemNum - 1] : t._calcExistItemPos(t._numItems - 1);
  1964. if (data && data.id == t._numItems - 1) {
  1965. center = t._sizeType ? ((data.top + data.bottom) / 2) : (center = (data.left + data.right) / 2);
  1966. switch (t._alignCalcType) {
  1967. case 1://单行HORIZONTAL(LEFT_TO_RIGHT)、网格VERTICAL(LEFT_TO_RIGHT)
  1968. if (vRight > center)
  1969. t.nearestListId = data.id;
  1970. break;
  1971. case 2://单行HORIZONTAL(RIGHT_TO_LEFT)、网格VERTICAL(RIGHT_TO_LEFT)
  1972. if (vLeft < center)
  1973. t.nearestListId = data.id;
  1974. break;
  1975. case 3://单列VERTICAL(TOP_TO_BOTTOM)、网格HORIZONTAL(TOP_TO_BOTTOM)
  1976. if (vBottom < center)
  1977. t.nearestListId = data.id;
  1978. break;
  1979. case 4://单列VERTICAL(BOTTOM_TO_TOP)、网格HORIZONTAL(BOTTOM_TO_TOP)
  1980. if (vTop > center)
  1981. t.nearestListId = data.id;
  1982. break;
  1983. }
  1984. }
  1985. // cc.log('t.nearestListId =', t.nearestListId);
  1986. }
  1987. //上一页
  1988. prePage(timeInSecond: number = .5) {
  1989. // cc.log('👈');
  1990. if (!this.checkInited())
  1991. return;
  1992. this.skipPage(this.curPageNum - 1, timeInSecond);
  1993. }
  1994. //下一页
  1995. nextPage(timeInSecond: number = .5) {
  1996. // cc.log('👉');
  1997. if (!this.checkInited())
  1998. return;
  1999. this.skipPage(this.curPageNum + 1, timeInSecond);
  2000. }
  2001. //跳转到第几页
  2002. skipPage(pageNum: number, timeInSecond: number) {
  2003. let t: any = this;
  2004. if (!t.checkInited())
  2005. return;
  2006. if (t._slideMode != SlideType.PAGE)
  2007. return console.error('This function is not allowed to be called, Must SlideMode = PAGE!');
  2008. if (pageNum < 0 || pageNum >= t._numItems)
  2009. return;
  2010. if (t.curPageNum == pageNum)
  2011. return;
  2012. // cc.log(pageNum);
  2013. t.curPageNum = pageNum;
  2014. if (t.pageChangeEvent) {
  2015. EventHandler.emitEvents([t.pageChangeEvent], pageNum);
  2016. }
  2017. t.scrollTo(pageNum, timeInSecond);
  2018. }
  2019. //计算 CustomSize(这个函数还是保留吧,某些罕见的情况的确还是需要手动计算customSize的)
  2020. calcCustomSize(numItems: number) {
  2021. let t: any = this;
  2022. if (!t.checkInited())
  2023. return;
  2024. if (!t._itemTmp)
  2025. return console.error('Unset template item!');
  2026. if (!t.renderEvent)
  2027. return console.error('Unset Render-Event!');
  2028. t._customSize = {};
  2029. let temp: any = instantiate(t._itemTmp);
  2030. let ut: UITransform = temp.getComponent(UITransform);
  2031. t.content.addChild(temp);
  2032. for (let n: number = 0; n < numItems; n++) {
  2033. EventHandler.emitEvents([t.renderEvent], temp, n);
  2034. if (ut.height != t._itemSize.height || ut.width != t._itemSize.width) {
  2035. t._customSize[n] = t._sizeType ? ut.height : ut.width;
  2036. }
  2037. }
  2038. if (!Object.keys(t._customSize).length)
  2039. t._customSize = null;
  2040. temp.removeFromParent();
  2041. if (temp.destroy)
  2042. temp.destroy();
  2043. return t._customSize;
  2044. }
  2045. }