List.ts 84 KB

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