List.ts 88 KB

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