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