Laya 实现带有子菜单的List
Laya 用List实现固定大小的二级菜单
1.继承修改了List
1 /**
2 * 改良List
3 * 改良目标:单列列表,item不规则大小
4 *
5 * @author ixenos 2020-10-27 14:40:00
6 *
7 */
8 public class CList extends List
9 {
10 /**单元格渲染定位处理器(默认返回参数 _isVertical:Boolean,cell:Box,pos:Number,index:int)*/
11 public var posHandler:Handler;
12 /**数据单元位置缓存,设计给单列列表使用,多列列表再斟酌一下*/
13 private var posCache:Array = [];
14
15 public function CList()
16 {
17 super();
18 }
19
20 /**
21 * 渲染一个单元格。
22 * @param cell 需要渲染的单元格对象。
23 * @param index 单元格索引。
24 */
25 override protected function renderItem(cell:*, index:int):void {
26 if (_array && index >= 0 && index < _array.length) {
27 cell.visible = true;
28
29 if (cell._$bindData) {
30 cell._dataSource = _array[index];
31 _bindData(cell, _array[index]);
32 } else cell.dataSource = _array[index];
33
34 if (!cacheContent) {
35 //TODO:
36 posCell(cell, index);
37 }
38 if (hasListener(Event.RENDER)) event(Event.RENDER, [cell, index]);
39 if (renderHandler) renderHandler.runWith([cell, index]);
40 } else {
41 cell.visible = false;
42 cell.dataSource = null;
43 }
44 }
45
46 /**
47 * @private
48 * 滚动条的 Event.CHANGE
事件侦听处理函数。
49 */
50 override protected function onScrollBarChange(e:Event = null):void {
51 runCallLater(changeCells);
52 var scrollValue:Number = _scrollBar.value;
53 var lineX:int = (_isVertical ? this.repeatX : this.repeatY);
54 var lineY:int = (_isVertical ? this.repeatY : this.repeatX);
55 var scrollLine:int = Math.floor(scrollValue / _cellSize);
56
57 //CList: 增加序号精度, 仅限用于单列列表 --ixenos 2020-10-28 09:47:22
58 if(posCache && posCache.length>0){
59 for (var j:int = 0; j < posCache.length; j++) {
60 var pos:Number = posCache[j];
61 var lastPos:Number = j-1<0?0:posCache[j-1];
62 if(scrollValue <= pos){
63 var len:Number = pos - lastPos;
64 var len1:Number = scrollValue - lastPos;
65 if(len1>len*99/100){
66 scrollLine = j;
67 }else{
68 scrollLine = j-1;
69 }
70
71 break;
72 }
73 }
74 }
75
76 if (!cacheContent) {
77 var index:int = scrollLine * lineX;
78 var num:int = 0;
79
80 if (index > _startIndex) {
81 num = index - _startIndex;
82 var down:Boolean = true;
83 var toIndex:int = _startIndex + lineX * (lineY + 1);
84 _isMoved = true;
85 } else if (index < _startIndex) {
86 num = _startIndex - index;
87 down = false;
88 toIndex = _startIndex - 1;
89 _isMoved = true;
90 }
91
92 for (var i:int = 0; i < num; i++) {
93 if (down) {
94 var cell:Box = _cells.shift();
95 _cells[_cells.length] = cell;
96 var cellIndex:int = toIndex + i;
97 } else {
98 cell = _cells.pop();
99 _cells.unshift(cell);
100 cellIndex = toIndex - i;
101 }
102 pos = Math.floor(cellIndex / lineX) * _cellSize;
103 _isVertical ? cell.y = pos : cell.x = pos;
104 renderItem(cell, cellIndex);
105 }
106 _startIndex = index;
107 changeSelectStatus();
108 } else {
109 num = (lineY + 1);
110 if (_createdLine - scrollLine < num) {
111 _createItems(_createdLine, lineX, _createdLine + num);
112 renderItems(_createdLine * lineX, 0);
113 _createdLine += num;
114 }
115 }
116
117 var r:Rectangle = _content.scrollRect;
118 if (_isVertical) {
119 r.y = scrollValue - _offset.y;
120 r.x = -_offset.x;
121 } else {
122 r.y = -_offset.y;
123 r.x = scrollValue - _offset.x;
124 }
125 _content.scrollRect = r;
126 }
127
128 private function _createItems(startY:int, numX:int, numY:int):void {
129 var box:Box = _content;
130 var cell:Box = _getOneCell();
131 var cellWidth:Number = cell.width + _spaceX;
132 var cellHeight:Number = cell.height + _spaceY;
133
134 if (cacheContent) {
135 var cacheBox:Box = new Box();
136 cacheBox.cacheAsBitmap = true;
137 cacheBox.pos((_isVertical ? 0 : startY) * cellWidth, (_isVertical ? startY : 0) * cellHeight);
138 _content.addChild(cacheBox);
139 _content.optimizeScrollRect = true;
140 box = cacheBox;
141 } else {
142 var arr:Array = [];
143 for (var i:int = _cells.length - 1; i > -1; i--) {
144 var item:Box = _cells[i];
145 item.removeSelf();
146 arr.push(item);
147 }
148 _cells.length = 0;
149 }
150
151 for (var k:int = startY; k < numY; k++) {
152 for (var l:int = 0; l < numX; l++) {
153 if (arr && arr.length) {
154 cell = arr.pop();
155 } else {
156 cell = createItem();
157 }
158 cell.x = (_isVertical ? l : k) * cellWidth - box.x;
159 cell.y = (_isVertical ? k : l) * cellHeight - box.y;
160 cell.name = "item" + (k * numX + l);
161 box.addChild(cell);
162 addCell(cell);
163 }
164 }
165 }
166
167 private function _getOneCell():Box {
168 if (_cells.length === 0) {
169 var item:Box = createItem();
170 _offset.setTo(item.x, item.y);
171 if (cacheContent) return item;
172 _cells.push(item);
173 }
174 return _cells[0];
175 }
176
177 private function posCell(cell:Box, cellIndex:int):void {
178 if (!_scrollBar) return;
179 var lineX:int = (_isVertical ? this.repeatX : this.repeatY);
180 var lineY:int = (_isVertical ? this.repeatY : this.repeatX);
181 var pos:Number = Math.floor(cellIndex / lineX) * _cellSize;
182 _isVertical ? cell.y = pos : cell.x = pos;
183
184 //CList: 新增posHandler --ixenos 2020-10-27 15:00:17
185 posHandler && posHandler.runWith([_isVertical, cell, pos, cellIndex]);
186 posCache[cellIndex] = cell.y;
187 }
188
189 private function _bindData(cell:*, data:Object):void {
190 var arr:Array = cell._$bindData;
191 for (var i:int = 0, n:int = arr.length; i < n; i++) {
192 var ele:* = arr[i++];
193 var prop:String = arr[i++];
194 var value:String = arr[i];
195 var fun:Function = UIUtils.getBindFun(value);
196 ele[prop] = fun.call(this, data);
197 }
198 }
199
200 /**
201 * @private
202 * 更改单元格的信息。
203 * @internal 在此销毁、创建单元格,并设置单元格的位置等属性。相当于此列表内容发送改变时调用此函数。
204 */
205 override protected function changeCells():void {
206 _cellChanged = false;
207 if (_itemRender) {
208 //获取滚动条
209 scrollBar = getChildByName("scrollBar") as ScrollBar;
210
211 //自适应宽高
212 var cell:Box = _getOneCell();
213
214 var cellWidth:Number = (cell.width + _spaceX) || 1;
215 var cellHeight:Number = (cell.height + _spaceY) || 1;
216 if (_width > 0) _repeatX2 = _isVertical ? Math.round(_width / cellWidth) : Math.ceil(_width / cellWidth);
217 if (_height > 0) _repeatY2 = _isVertical ? Math.ceil(_height / cellHeight) : Math.round(_height / cellHeight);
218
219 var listWidth:Number = _width ? _width : (cellWidth * repeatX - _spaceX);
220 var listHeight:Number = _height ? _height : (cellHeight * repeatY - _spaceY);
221 _cellSize = _isVertical ? cellHeight : cellWidth;
222 _cellOffset = _isVertical ? (cellHeight * Math.max(_repeatY2, _repeatY) - listHeight - _spaceY) : (cellWidth * Math.max(_repeatX2, _repeatX) - listWidth - _spaceX);
223
224 if (_isVertical && _scrollBar) _scrollBar.height = listHeight;
225 else if (!_isVertical && _scrollBar) _scrollBar.width = listWidth;
226 setContentSize(listWidth, listHeight);
227
228 //创建新单元格
229 var numX:int = _isVertical ? repeatX : repeatY;
230 var numY:int = (_isVertical ? repeatY : repeatX) + (_scrollBar ? 1 : 0);
231 _createItems(0, numX, numY);
232 _createdLine = numY;
233
234 if (_array) {
235 array = _array;
236 runCallLater(renderItems);
237 }
238 }
239 }
240 }
2.业务伪代码
1 private function init():void 2 { 3 lui.listCate.posHandler = Handler.create(this, listCatePos, null, false); 4 lui.listCate.repeatY = 4; 5 lui.listCate.scrollBar.max = getListContentHeight(); 6 } 7 8 private function onToggleCate(index:int):void{ 9 var dat:* = lui.listCate.getItem(index); 10 dat["open"] = !dat["open"]; 11 lui.listCate.setItem(index, dat); 12 lui.listCate.refresh(); 13 lui.listCate.scrollBar.max = getListContentHeight(); 14 } 15 16 private function getOpenNumBeforeCurIdx(curIdx:int):int{ 17 var ret:int = 0; 18 for (var i:int = 0; i < curIdx; i++) { 19 var dat:* = lui.listCate.getItem(i); 20 if(dat && dat["open"]){ 21 ret++; 22 } 23 } 24 return ret; 25 } 26 27 private function getListContentHeight():Number{ 28 var ret:Number = 130 * lui.listCate.length + lui.listCate.spaceY * (lui.listCate.length - 1) - 440;//TODO 这个值需要参数绑定 29 for (var i:int = 0; i < lui.listCate.length; i++) { 30 var dat:* = lui.listCate.getItem(i); 31 ret += dat["open"]?_subH:0; 32 } 33 ret -= lui.listCate.spaceY; 34 return ret; 35 } 36 37 private function listCatePos(isVertical:Boolean,cell:Box, pos:Number, index:int):Number{ 38 var totalOpenNum:int = getOpenNumBeforeCurIdx(index); 39 var closeLength:Number = (index - totalOpenNum) * _subH; 40 pos -= closeLength; 41 if(isVertical){ 42 cell.y = pos; 43 }else{ 44 cell.x = pos; 45 } 46 47 return pos; 48 } 49 50 private function listCateRender(cell:StageSelectItem, index:int):void{ 51 var dat:* = lui.listCate.getItem(index); 52 var isOpen:Boolean = dat["open"]; 53 var totalOpenNum:int = getOpenNumBeforeCurIdx(index); 54 cell.updateView(index, isOpen, totalOpenNum); 55 cell.toggleHandler = Handler.create(this, onToggleCate, [index], false); 56 }