List.ts 88 KB

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