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