1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 /** 8 * @class Container是所有容器类的基类。每个Container都可以添加其他可视对象为子级。 9 * @augments View 10 * @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。 11 * @module hilo/view/Container 12 * @requires hilo/core/Hilo 13 * @requires hilo/core/Class 14 * @requires hilo/view/View 15 * @property {Array} children 容器的子元素列表。只读。 16 * @property {Boolean} pointerChildren 指示容器的子元素是否能响应用户交互事件。默认为true。 17 * @property {Boolean} clipChildren 指示是否裁剪超出容器范围的子元素。默认为false。 18 */ 19 var Container = Class.create(/** @lends Container.prototype */{ 20 Extends: View, 21 constructor: function(properties){ 22 properties = properties || {}; 23 this.id = this.id || properties.id || Hilo.getUid("Container"); 24 Container.superclass.constructor.call(this, properties); 25 26 if(this.children) this._updateChildren(); 27 else this.children = []; 28 }, 29 30 children: null, 31 pointerChildren: true, 32 clipChildren: false, 33 34 /** 35 * 返回容器的子元素的数量。 36 * @returns {Uint} 容器的子元素的数量。 37 */ 38 getNumChildren: function(){ 39 return this.children.length; 40 }, 41 42 /** 43 * 在指定索引位置添加子元素。 44 * @param {View} child 要添加的子元素。 45 * @param {Number} index 指定的索引位置,从0开始。 46 */ 47 addChildAt: function(child, index){ 48 var children = this.children, 49 len = children.length, 50 parent = child.parent; 51 52 index = index < 0 ? 0 : index > len ? len : index; 53 var childIndex = this.getChildIndex(child); 54 if(childIndex == index){ 55 return this; 56 }else if(childIndex >= 0){ 57 children.splice(childIndex, 1); 58 index = index == len ? len - 1 : index; 59 }else if(parent){ 60 parent.removeChild(child); 61 } 62 63 children.splice(index, 0, child); 64 65 //直接插入,影响插入位置之后的深度 66 if(childIndex < 0){ 67 this._updateChildren(index); 68 } 69 //只是移动时影响中间段的深度 70 else{ 71 var startIndex = childIndex < index ? childIndex : index; 72 var endIndex = childIndex < index ? index : childIndex;; 73 this._updateChildren(startIndex, endIndex + 1); 74 } 75 76 return this; 77 }, 78 79 /** 80 * 在最上面添加子元素。 81 * @param {View} child 要添加的子元素。 82 */ 83 addChild: function(child){ 84 var total = this.children.length, 85 args = arguments; 86 87 for(var i = 0, len = args.length; i < len; i++){ 88 this.addChildAt(args[i], total + i); 89 } 90 return this; 91 }, 92 93 /** 94 * 在指定索引位置删除子元素。 95 * @param {Int} index 指定删除元素的索引位置,从0开始。 96 * @returns {View} 被删除的对象。 97 */ 98 removeChildAt: function(index){ 99 var children = this.children; 100 if(index < 0 || index >= children.length) return null; 101 102 var child = children[index]; 103 if(child){ 104 //NOTE: use `__renderer` for fixing child removal (DOMRenderer and FlashRenderer only). 105 //Do `not` use it in any other case. 106 if(!child.__renderer){ 107 var obj = child; 108 while(obj = obj.parent){ 109 //obj is stage 110 if(obj.renderer){ 111 child.__renderer = obj.renderer; 112 break; 113 } 114 else if(obj.__renderer){ 115 child.__renderer = obj.__renderer; 116 break; 117 } 118 } 119 } 120 121 if(child.__renderer){ 122 child.__renderer.remove(child); 123 } 124 125 child.parent = null; 126 child.depth = -1; 127 } 128 129 children.splice(index, 1); 130 this._updateChildren(index); 131 132 return child; 133 }, 134 135 /** 136 * 删除指定的子元素。 137 * @param {View} child 指定要删除的子元素。 138 * @returns {View} 被删除的对象。 139 */ 140 removeChild: function(child){ 141 return this.removeChildAt(this.getChildIndex(child)); 142 }, 143 144 /** 145 * 删除指定id的子元素。 146 * @param {String} id 指定要删除的子元素的id。 147 * @returns {View} 被删除的对象。 148 */ 149 removeChildById: function(id){ 150 var children = this.children, child; 151 for(var i = 0, len = children.length; i < len; i++){ 152 child = children[i]; 153 if(child.id === id){ 154 this.removeChildAt(i); 155 return child; 156 } 157 } 158 return null; 159 }, 160 161 /** 162 * 删除所有的子元素。 163 * @returns {Container} 容器本身。 164 */ 165 removeAllChildren: function(){ 166 while(this.children.length) this.removeChildAt(0); 167 return this; 168 }, 169 170 /** 171 * 返回指定索引位置的子元素。 172 * @param {Number} index 指定要返回的子元素的索引值,从0开始。 173 */ 174 getChildAt: function(index){ 175 var children = this.children; 176 if(index < 0 || index >= children.length) return null; 177 return children[index]; 178 }, 179 180 /** 181 * 返回指定id的子元素。 182 * @param {String} id 指定要返回的子元素的id。 183 */ 184 getChildById: function(id){ 185 var children = this.children, child; 186 for(var i = 0, len = children.length; i < len; i++){ 187 child = children[i]; 188 if(child.id === id) return child; 189 } 190 return null; 191 }, 192 193 /** 194 * 返回指定子元素的索引值。 195 * @param {View} child 指定要返回索引值的子元素。 196 */ 197 getChildIndex: function(child){ 198 return this.children.indexOf(child); 199 }, 200 201 /** 202 * 设置子元素的索引位置。 203 * @param {View} child 指定要设置的子元素。 204 * @param {Number} index 指定要设置的索引值。 205 */ 206 setChildIndex: function(child, index){ 207 var children = this.children, 208 oldIndex = children.indexOf(child); 209 210 if(oldIndex >= 0 && oldIndex != index){ 211 var len = children.length; 212 index = index < 0 ? 0 : index >= len ? len - 1 : index; 213 children.splice(oldIndex, 1); 214 children.splice(index, 0, child); 215 this._updateChildren(); 216 } 217 return this; 218 }, 219 220 /** 221 * 交换两个子元素的索引位置。 222 * @param {View} child1 指定要交换的子元素A。 223 * @param {View} child2 指定要交换的子元素B。 224 */ 225 swapChildren: function(child1, child2){ 226 var children = this.children, 227 index1 = this.getChildIndex(child1), 228 index2 = this.getChildIndex(child2); 229 230 child1.depth = index2; 231 children[index2] = child1; 232 child2.depth = index1; 233 children[index1] = child2; 234 }, 235 236 /** 237 * 交换两个指定索引位置的子元素。 238 * @param {Number} index1 指定要交换的索引位置A。 239 * @param {Number} index2 指定要交换的索引位置B。 240 */ 241 swapChildrenAt: function(index1, index2){ 242 var children = this.children, 243 child1 = this.getChildAt(index1), 244 child2 = this.getChildAt(index2); 245 246 child1.depth = index2; 247 children[index2] = child1; 248 child2.depth = index1; 249 children[index1] = child2; 250 }, 251 252 /** 253 * 根据指定键值或函数对子元素进行排序。 254 * @param {Object} keyOrFunction 如果此参数为String时,则根据子元素的某个属性值进行排序;如果此参数为Function时,则根据此函数进行排序。 255 */ 256 sortChildren: function(keyOrFunction){ 257 var fn = keyOrFunction, 258 children = this.children; 259 if(typeof fn == "string"){ 260 var key = fn; 261 fn = function(a, b){ 262 return b[key] - a[key]; 263 }; 264 } 265 children.sort(fn); 266 this._updateChildren(); 267 }, 268 269 /** 270 * 更新子元素。 271 * @private 272 */ 273 _updateChildren: function(start, end){ 274 var children = this.children, child, 275 start = start || 0, 276 end = end || children.length; 277 for(var i = start; i < end; i++){ 278 child = children[i]; 279 child.depth = i + 1; 280 child.parent = this; 281 } 282 }, 283 284 /** 285 * 返回是否包含参数指定的子元素。 286 * @param {View} child 指定要测试的子元素。 287 */ 288 contains: function(child){ 289 while(child = child.parent){ 290 if(child === this){ 291 return true; 292 } 293 } 294 return false; 295 }, 296 297 /** 298 * 返回由x和y指定的点下的对象。 299 * @param {Number} x 指定点的x轴坐标。 300 * @param {Number} y 指定点的y轴坐标。 301 * @param {Boolean} usePolyCollision 指定是否使用多边形碰撞检测。默认为false。 302 * @param {Boolean} global 使用此标志表明将查找所有符合的对象,而不仅仅是第一个,即全局匹配。默认为false。 303 * @param {Boolean} eventMode 使用此标志表明将在事件模式下查找对象。默认为false。 304 */ 305 getViewAtPoint: function(x, y, usePolyCollision, global, eventMode){ 306 var result = global ? [] : null, 307 children = this.children, child, obj; 308 309 for(var i = children.length - 1; i >= 0; i--){ 310 child = children[i]; 311 //skip child which is not shown or pointer enabled 312 if(!child || !child.visible || child.alpha <= 0 || (eventMode && !child.pointerEnabled)) continue; 313 //find child recursively 314 if(child.children && child.children.length && !(eventMode && !child.pointerChildren)){ 315 obj = child.getViewAtPoint(x, y, usePolyCollision, global, eventMode); 316 } 317 318 if(obj){ 319 if(!global) return obj; 320 else if(obj.length) result = result.concat(obj); 321 }else if(child.hitTestPoint(x, y, usePolyCollision)){ 322 if(!global) return child; 323 else result.push(child); 324 } 325 } 326 327 return global && result.length ? result : null; 328 }, 329 330 /** 331 * 覆盖渲染方法。 332 * @private 333 */ 334 render: function(renderer, delta){ 335 Container.superclass.render.call(this, renderer, delta); 336 337 var children = this.children.slice(0), i, len, child; 338 for(i = 0, len = children.length; i < len; i++){ 339 child = children[i]; 340 //NOTE: the child could remove or change it's parent 341 if(child.parent === this) child._render(renderer, delta); 342 } 343 } 344 345 });