List.ts 93 KB

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