1 /**************************************************************************** 2 Copyright (c) 2011-2012 cocos2d-x.org 3 Copyright (c) 2013-2014 Chukong Technologies Inc. 4 5 http://www.cocos2d-x.org 6 7 Permission is hereby granted, free of charge, to any person obtaining a copy 8 of this software and associated documentation files (the "Software"), to deal 9 in the Software without restriction, including without limitation the rights 10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 copies of the Software, and to permit persons to whom the Software is 12 furnished to do so, subject to the following conditions: 13 14 The above copyright notice and this permission notice shall be included in 15 all copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 THE SOFTWARE. 24 ****************************************************************************/ 25 26 /** 27 * The list view control of Cocos UI. 28 * @class 29 * @extends ccui.ScrollView 30 * @example 31 * var listView = new ccui.ListView(); 32 * // set list view ex direction 33 * listView.setDirection(ccui.ScrollView.DIR_VERTICAL); 34 * listView.setTouchEnabled(true); 35 * listView.setBounceEnabled(true); 36 * listView.setBackGroundImage("res/cocosui/green_edit.png"); 37 * listView.setBackGroundImageScale9Enabled(true); 38 * listView.setContentSize(cc.size(240, 130)); 39 * this.addChild(listView); 40 */ 41 ccui.ListView = ccui.ScrollView.extend(/** @lends ccui.ListView# */{ 42 _model: null, 43 _items: null, 44 _gravity: null, 45 _itemsMargin: 0, 46 47 _curSelectedIndex: 0, 48 _refreshViewDirty: true, 49 50 _listViewEventListener: null, 51 _listViewEventSelector: null, 52 /** 53 * allocates and initializes a UIListView. 54 * Constructor of ccui.ListView, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. 55 * @example 56 * // example 57 * var aListView = new ccui.ListView(); 58 */ 59 ctor: function () { 60 ccui.ScrollView.prototype.ctor.call(this); 61 this._items = []; 62 this._gravity = ccui.ListView.GRAVITY_CENTER_VERTICAL; 63 this.setTouchEnabled(true); 64 65 this.init(); 66 }, 67 68 /** 69 * Initializes a ccui.ListView. Please do not call this function by yourself, you should pass the parameters to constructor to initialize it. 70 * @returns {boolean} 71 * @override 72 */ 73 init: function () { 74 if (ccui.ScrollView.prototype.init.call(this)) { 75 this.setLayoutType(ccui.Layout.LINEAR_VERTICAL); 76 return true; 77 } 78 return false; 79 }, 80 81 /** 82 * Sets a item model for ListView. A model will be cloned for adding default item. 83 * @param {ccui.Widget} model 84 */ 85 setItemModel: function (model) { 86 if (!model){ 87 cc.log("Can't set a null to item model!"); 88 return; 89 } 90 91 this._model = model; 92 }, 93 94 _updateInnerContainerSize: function () { 95 var locItems = this._items, length, i; 96 switch (this.direction) { 97 case ccui.ScrollView.DIR_VERTICAL: 98 length = locItems.length; 99 var totalHeight = (length - 1) * this._itemsMargin; 100 for (i = 0; i < length; i++) { 101 totalHeight += locItems[i].getContentSize().height; 102 } 103 this.setInnerContainerSize(cc.size(this._contentSize.width, totalHeight)); 104 break; 105 case ccui.ScrollView.DIR_HORIZONTAL: 106 length = locItems.length; 107 var totalWidth = (length - 1) * this._itemsMargin; 108 for (i = 0; i < length; i++) { 109 totalWidth += locItems[i].getContentSize().width; 110 } 111 this.setInnerContainerSize(cc.size(totalWidth, this._contentSize.height)); 112 break; 113 default: 114 break; 115 } 116 }, 117 118 _remedyLayoutParameter: function (item) { 119 cc.assert(null != item, "ListView Item can't be nil!"); 120 121 var linearLayoutParameter = item.getLayoutParameter(); 122 var isLayoutParameterExists = true; 123 if (!linearLayoutParameter) { 124 linearLayoutParameter = new ccui.LinearLayoutParameter(); 125 isLayoutParameterExists = false; 126 } 127 var itemIndex = this.getIndex(item); 128 switch (this.direction) { 129 case ccui.ScrollView.DIR_VERTICAL: 130 this._remedyVerticalLayoutParameter(linearLayoutParameter, itemIndex); 131 break; 132 case ccui.ScrollView.DIR_HORIZONTAL: 133 this._remedyHorizontalLayoutParameter(linearLayoutParameter, itemIndex); 134 break; 135 default: 136 break; 137 } 138 if (!isLayoutParameterExists) 139 item.setLayoutParameter(linearLayoutParameter); 140 }, 141 142 //@since v3.3 143 _remedyVerticalLayoutParameter: function (layoutParameter, itemIndex) { 144 cc.assert(null != layoutParameter, "Layout parameter can't be nil!"); 145 146 switch (this._gravity) { 147 case ccui.ListView.GRAVITY_LEFT: 148 layoutParameter.setGravity(ccui.LinearLayoutParameter.LEFT); 149 break; 150 case ccui.ListView.GRAVITY_RIGHT: 151 layoutParameter.setGravity(ccui.LinearLayoutParameter.RIGHT); 152 break; 153 case ccui.ListView.GRAVITY_CENTER_HORIZONTAL: 154 layoutParameter.setGravity(ccui.LinearLayoutParameter.CENTER_HORIZONTAL); 155 break; 156 default: 157 break; 158 } 159 if (0 === itemIndex) 160 layoutParameter.setMargin(ccui.MarginZero()); 161 else 162 layoutParameter.setMargin(new ccui.Margin(0.0, this._itemsMargin, 0.0, 0.0)); 163 }, 164 165 //@since v3.3 166 _remedyHorizontalLayoutParameter: function (layoutParameter, itemIndex) { 167 cc.assert(null != layoutParameter, "Layout parameter can't be nil!"); 168 169 switch (this._gravity) { 170 case ccui.ListView.GRAVITY_TOP: 171 layoutParameter.setGravity(ccui.LinearLayoutParameter.TOP); 172 break; 173 case ccui.ListView.GRAVITY_BOTTOM: 174 layoutParameter.setGravity(ccui.LinearLayoutParameter.BOTTOM); 175 break; 176 case ccui.ListView.GRAVITY_CENTER_VERTICAL: 177 layoutParameter.setGravity(ccui.LinearLayoutParameter.CENTER_VERTICAL); 178 break; 179 default: 180 break; 181 } 182 if (0 === itemIndex) 183 layoutParameter.setMargin(ccui.MarginZero()); 184 else 185 layoutParameter.setMargin(new ccui.Margin(this._itemsMargin, 0.0, 0.0, 0.0)); 186 }, 187 188 /** 189 * Push back a default item(create by a cloned model) into ListView. 190 */ 191 pushBackDefaultItem: function () { 192 if (this._model == null) 193 return; 194 var newItem = this._model.clone(); 195 this._remedyLayoutParameter(newItem); 196 this.addChild(newItem); 197 this._refreshViewDirty = true; 198 }, 199 200 /** 201 * Insert a default item(create by a cloned model) into ListView. 202 * @param {Number} index 203 */ 204 insertDefaultItem: function (index) { 205 if (this._model == null) 206 return; 207 var newItem = this._model.clone(); 208 this._items.splice(index, 0, newItem); 209 ccui.ScrollView.prototype.addChild.call(this, newItem); 210 this._remedyLayoutParameter(newItem); 211 212 this._refreshViewDirty = true; 213 }, 214 215 /** 216 * Push back custom item into ListView. 217 * @param {ccui.Widget} item 218 */ 219 pushBackCustomItem: function (item) { 220 this._remedyLayoutParameter(item); 221 this.addChild(item); 222 this._refreshViewDirty = true; 223 }, 224 225 /** 226 * add child to ListView 227 * @override 228 * @param {cc.Node} widget 229 * @param {Number} [zOrder] 230 * @param {Number|String} [tag] tag or name 231 */ 232 addChild: function (widget, zOrder, tag) { 233 if (widget) { 234 zOrder = zOrder || widget.getLocalZOrder(); 235 tag = tag || widget.getName(); 236 ccui.ScrollView.prototype.addChild.call(this, widget, zOrder, tag); 237 if(widget instanceof ccui.Widget) 238 this._items.push(widget); 239 } 240 }, 241 242 /** 243 * remove child from ListView 244 * @override 245 * @param {cc.Node} widget 246 * @param {Boolean} [cleanup=true] 247 */ 248 removeChild: function(widget, cleanup){ 249 if (widget) { 250 var index = this._items.indexOf(widget); 251 if(index > -1) 252 this._items.splice(index, 1); 253 ccui.ScrollView.prototype.removeChild.call(this, widget, cleanup); 254 } 255 }, 256 257 /** 258 * Removes all children from ccui.ListView. 259 */ 260 removeAllChildren: function(){ 261 this.removeAllChildrenWithCleanup(true); 262 }, 263 264 /** 265 * Removes all children from ccui.ListView and do a cleanup all running actions depending on the cleanup parameter. 266 * @param {Boolean} cleanup 267 */ 268 removeAllChildrenWithCleanup: function(cleanup){ 269 ccui.ScrollView.prototype.removeAllChildrenWithCleanup.call(this, cleanup); 270 this._items = []; 271 }, 272 273 /** 274 * Push back custom item into ccui.ListView. 275 * @param {ccui.Widget} item 276 * @param {Number} index 277 */ 278 insertCustomItem: function (item, index) { 279 this._items.splice(index, 0, item); 280 ccui.ScrollView.prototype.addChild.call(this, item); 281 this._remedyLayoutParameter(item); 282 this._refreshViewDirty = true; 283 }, 284 285 /** 286 * Removes a item whose index is same as the parameter. 287 * @param {Number} index 288 */ 289 removeItem: function (index) { 290 var item = this.getItem(index); 291 if (item == null) 292 return; 293 this.removeChild(item, true); 294 this._refreshViewDirty = true; 295 }, 296 297 /** 298 * Removes the last item of ccui.ListView. 299 */ 300 removeLastItem: function () { 301 this.removeItem(this._items.length - 1); 302 }, 303 304 /** 305 * Removes all items from ccui.ListView. 306 */ 307 removeAllItems: function(){ 308 this.removeAllChildren(); 309 }, 310 311 /** 312 * Returns a item whose index is same as the parameter. 313 * @param {Number} index 314 * @returns {ccui.Widget} 315 */ 316 getItem: function (index) { 317 if (index < 0 || index >= this._items.length) 318 return null; 319 return this._items[index]; 320 }, 321 322 /** 323 * Returns the item container. 324 * @returns {Array} 325 */ 326 getItems: function () { 327 return this._items; 328 }, 329 330 /** 331 * Returns the index of item. 332 * @param {ccui.Widget} item the item which need to be checked. 333 * @returns {Number} the index of item. 334 */ 335 getIndex: function (item) { 336 if(item == null) 337 return -1; 338 return this._items.indexOf(item); 339 }, 340 341 /** 342 * Changes the gravity of ListView. 343 * @param {ccui.ListView.GRAVITY_LEFT|ccui.ListView.GRAVITY_RIGHT|ccui.ListView.GRAVITY_CENTER_HORIZONTAL|ccui.ListView.GRAVITY_BOTTOM|ccui.ListView.GRAVITY_CENTER_VERTICAL} gravity 344 */ 345 setGravity: function (gravity) { 346 if (this._gravity === gravity) 347 return; 348 this._gravity = gravity; 349 this._refreshViewDirty = true; 350 }, 351 352 /** 353 * Changes the margin between each item. 354 * @param {Number} margin 355 */ 356 setItemsMargin: function (margin) { 357 if (this._itemsMargin === margin) 358 return; 359 this._itemsMargin = margin; 360 this._refreshViewDirty = true; 361 }, 362 363 /** 364 * Returns the margin between each item. 365 * @returns {Number} 366 */ 367 getItemsMargin:function(){ 368 return this._itemsMargin; 369 }, 370 371 /** 372 * Changes scroll direction of ccui.ListView. 373 * @param {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} dir 374 */ 375 setDirection: function (dir) { 376 switch (dir) { 377 case ccui.ScrollView.DIR_VERTICAL: 378 this.setLayoutType(ccui.Layout.LINEAR_VERTICAL); 379 break; 380 case ccui.ScrollView.DIR_HORIZONTAL: 381 this.setLayoutType(ccui.Layout.LINEAR_HORIZONTAL); 382 break; 383 case ccui.ScrollView.DIR_BOTH: 384 return; 385 default: 386 return; 387 break; 388 } 389 ccui.ScrollView.prototype.setDirection.call(this, dir); 390 }, 391 392 /** 393 * Requests refresh list view. 394 */ 395 requestRefreshView: function () { 396 this._refreshViewDirty = true; 397 }, 398 399 /** 400 * Refreshes list view. 401 */ 402 refreshView: function () { 403 var locItems = this._items; 404 for (var i = 0; i < locItems.length; i++) { 405 var item = locItems[i]; 406 item.setLocalZOrder(i); 407 this._remedyLayoutParameter(item); 408 } 409 this._updateInnerContainerSize(); 410 }, 411 412 /** 413 * provides a public _doLayout function for Editor. it calls _doLayout. 414 */ 415 doLayout: function(){ 416 this._doLayout(); 417 }, 418 419 _doLayout: function(){ 420 ccui.Layout.prototype._doLayout.call(this); 421 if (this._refreshViewDirty) { 422 this.refreshView(); 423 this._refreshViewDirty = false; 424 } 425 }, 426 427 /** 428 * Adds event listener to ccui.ListView. 429 * @param {Function} selector 430 * @param {Object} [target=] 431 * @deprecated since v3.0, please use addEventListener instead. 432 */ 433 addEventListenerListView: function (selector, target) { 434 this.addEventListener(selector, target); 435 }, 436 437 /** 438 * Adds event listener to ccui.ListView. 439 * @param {Function} selector 440 * @param {Object} [target=] 441 */ 442 addEventListener: function(selector, target){ 443 this._listViewEventListener = target; 444 this._listViewEventSelector = selector; 445 }, 446 447 _selectedItemEvent: function (event) { 448 var eventEnum = (event === ccui.Widget.TOUCH_BEGAN) ? ccui.ListView.ON_SELECTED_ITEM_START : ccui.ListView.ON_SELECTED_ITEM_END; 449 if(this._listViewEventSelector){ 450 if (this._listViewEventListener) 451 this._listViewEventSelector.call(this._listViewEventListener, this, eventEnum); 452 else 453 this._listViewEventSelector(this, eventEnum); 454 } 455 if(this._ccEventCallback) 456 this._ccEventCallback(this, eventEnum); 457 }, 458 459 /** 460 * Intercept touch event, handle its child's touch event. 461 * @param {Number} eventType 462 * @param {ccui.Widget} sender 463 * @param {cc.Touch} touch 464 */ 465 interceptTouchEvent: function (eventType, sender, touch) { 466 ccui.ScrollView.prototype.interceptTouchEvent.call(this, eventType, sender, touch); 467 if(!this._touchEnabled) 468 { 469 return; 470 } 471 if (eventType !== ccui.Widget.TOUCH_MOVED) { 472 var parent = sender; 473 while (parent) { 474 if (parent && parent.getParent() === this._innerContainer) { 475 this._curSelectedIndex = this.getIndex(parent); 476 break; 477 } 478 parent = parent.getParent(); 479 } 480 if (sender.isHighlighted()) 481 this._selectedItemEvent(eventType); 482 } 483 }, 484 485 /** 486 * Returns current selected index 487 * @returns {number} 488 */ 489 getCurSelectedIndex: function () { 490 return this._curSelectedIndex; 491 }, 492 493 _onSizeChanged: function () { 494 ccui.ScrollView.prototype._onSizeChanged.call(this); 495 this._refreshViewDirty = true; 496 }, 497 498 /** 499 * Returns the "class name" of ccui.ListView. 500 * @returns {string} 501 */ 502 getDescription: function () { 503 return "ListView"; 504 }, 505 506 _createCloneInstance: function () { 507 return new ccui.ListView(); 508 }, 509 510 _copyClonedWidgetChildren: function (model) { 511 var arrayItems = model.getItems(); 512 for (var i = 0; i < arrayItems.length; i++) { 513 var item = arrayItems[i]; 514 this.pushBackCustomItem(item.clone()); 515 } 516 }, 517 518 _copySpecialProperties: function (listView) { 519 if(listView instanceof ccui.ListView){ 520 ccui.ScrollView.prototype._copySpecialProperties.call(this, listView); 521 this.setItemModel(listView._model); 522 this.setItemsMargin(listView._itemsMargin); 523 this.setGravity(listView._gravity); 524 525 this._listViewEventListener = listView._listViewEventListener; 526 this._listViewEventSelector = listView._listViewEventSelector; 527 } 528 }, 529 530 //v3.3 531 forceDoLayout: function(){ 532 if (this._refreshViewDirty) { 533 this.refreshView(); 534 this._refreshViewDirty = false; 535 } 536 this._innerContainer.forceDoLayout(); 537 } 538 }); 539 540 /** 541 * allocates and initializes a UIListView. 542 * @deprecated since v3.0, please use new ccui.ListView() instead. 543 */ 544 ccui.ListView.create = function () { 545 return new ccui.ListView(); 546 }; 547 548 // Constants 549 //listView event type 550 /** 551 * The flag selected item of ccui.ListView's event. 552 * @constant 553 * @type {number} 554 */ 555 ccui.ListView.EVENT_SELECTED_ITEM = 0; 556 557 /** 558 * The flag selected item start of ccui.ListView's event. 559 * @constant 560 * @type {number} 561 */ 562 ccui.ListView.ON_SELECTED_ITEM_START = 0; 563 /** 564 * The flag selected item end of ccui.ListView's event. 565 * @constant 566 * @type {number} 567 */ 568 ccui.ListView.ON_SELECTED_ITEM_END = 1; 569 570 //listView gravity 571 /** 572 * The left flag of ccui.ListView's gravity. 573 * @constant 574 * @type {number} 575 */ 576 ccui.ListView.GRAVITY_LEFT = 0; 577 /** 578 * The right flag of ccui.ListView's gravity. 579 * @constant 580 * @type {number} 581 */ 582 ccui.ListView.GRAVITY_RIGHT = 1; 583 /** 584 * The center horizontal flag of ccui.ListView's gravity. 585 * @constant 586 * @type {number} 587 */ 588 ccui.ListView.GRAVITY_CENTER_HORIZONTAL = 2; 589 /** 590 * The top flag of ccui.ListView's gravity. 591 * @constant 592 * @type {number} 593 */ 594 ccui.ListView.GRAVITY_TOP = 3; 595 /** 596 * The bottom flag of ccui.ListView's gravity. 597 * @constant 598 * @type {number} 599 */ 600 ccui.ListView.GRAVITY_BOTTOM = 4; 601 /** 602 * The center vertical flag of ccui.ListView's gravity. 603 * @constant 604 * @type {number} 605 */ 606 ccui.ListView.GRAVITY_CENTER_VERTICAL = 5;