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 _magneticAllowedOutOfBoundary: true, 54 _magneticType: 0, 55 _className:"ListView", 56 57 /** 58 * allocates and initializes a UIListView. 59 * Constructor of ccui.ListView, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. 60 * @example 61 * // example 62 * var aListView = new ccui.ListView(); 63 */ 64 ctor: function () { 65 this._items = []; 66 ccui.ScrollView.prototype.ctor.call(this); 67 this._gravity = ccui.ListView.GRAVITY_CENTER_VERTICAL; 68 this.setTouchEnabled(true); 69 this.setDirection(ccui.ScrollView.DIR_VERTICAL); 70 }, 71 72 /** 73 * Sets a item model for ListView. A model will be cloned for adding default item. 74 * @param {ccui.Widget} model 75 */ 76 setItemModel: function (model) { 77 if (!model){ 78 cc.log("Can't set a null to item model!"); 79 return; 80 } 81 82 this._model = model; 83 }, 84 85 _handleReleaseLogic: function(touch) 86 { 87 ccui.ScrollView.prototype._handleReleaseLogic.call(this, touch); 88 89 if(!this._autoScrolling) 90 { 91 this._startMagneticScroll(); 92 } 93 }, 94 95 _onItemListChanged: function() 96 { 97 this._outOfBoundaryAmountDirty = true; 98 }, 99 100 _updateInnerContainerSize: function () { 101 var locItems = this._items, length, i; 102 switch (this.direction) { 103 case ccui.ScrollView.DIR_VERTICAL: 104 length = locItems.length; 105 var totalHeight = (length - 1) * this._itemsMargin; 106 for (i = 0; i < length; i++) { 107 totalHeight += locItems[i].getContentSize().height; 108 } 109 this.setInnerContainerSize(cc.size(this._contentSize.width, totalHeight)); 110 break; 111 case ccui.ScrollView.DIR_HORIZONTAL: 112 length = locItems.length; 113 var totalWidth = (length - 1) * this._itemsMargin; 114 for (i = 0; i < length; i++) { 115 totalWidth += locItems[i].getContentSize().width; 116 } 117 this.setInnerContainerSize(cc.size(totalWidth, this._contentSize.height)); 118 break; 119 default: 120 break; 121 } 122 }, 123 124 _remedyLayoutParameter: function (item) { 125 cc.assert(null != item, "ListView Item can't be nil!"); 126 127 var linearLayoutParameter = item.getLayoutParameter(); 128 var isLayoutParameterExists = true; 129 if (!linearLayoutParameter) { 130 linearLayoutParameter = new ccui.LinearLayoutParameter(); 131 isLayoutParameterExists = false; 132 } 133 var itemIndex = this.getIndex(item); 134 switch (this.direction) { 135 case ccui.ScrollView.DIR_VERTICAL: 136 this._remedyVerticalLayoutParameter(linearLayoutParameter, itemIndex); 137 break; 138 case ccui.ScrollView.DIR_HORIZONTAL: 139 this._remedyHorizontalLayoutParameter(linearLayoutParameter, itemIndex); 140 break; 141 default: 142 break; 143 } 144 if (!isLayoutParameterExists) 145 item.setLayoutParameter(linearLayoutParameter); 146 }, 147 148 //@since v3.3 149 _remedyVerticalLayoutParameter: function (layoutParameter, itemIndex) { 150 cc.assert(null != layoutParameter, "Layout parameter can't be nil!"); 151 152 switch (this._gravity) { 153 case ccui.ListView.GRAVITY_LEFT: 154 layoutParameter.setGravity(ccui.LinearLayoutParameter.LEFT); 155 break; 156 case ccui.ListView.GRAVITY_RIGHT: 157 layoutParameter.setGravity(ccui.LinearLayoutParameter.RIGHT); 158 break; 159 case ccui.ListView.GRAVITY_CENTER_HORIZONTAL: 160 layoutParameter.setGravity(ccui.LinearLayoutParameter.CENTER_HORIZONTAL); 161 break; 162 default: 163 break; 164 } 165 if (0 === itemIndex) 166 layoutParameter.setMargin(ccui.MarginZero()); 167 else 168 layoutParameter.setMargin(new ccui.Margin(0.0, this._itemsMargin, 0.0, 0.0)); 169 }, 170 171 //@since v3.3 172 _remedyHorizontalLayoutParameter: function (layoutParameter, itemIndex) { 173 cc.assert(null != layoutParameter, "Layout parameter can't be nil!"); 174 175 switch (this._gravity) { 176 case ccui.ListView.GRAVITY_TOP: 177 layoutParameter.setGravity(ccui.LinearLayoutParameter.TOP); 178 break; 179 case ccui.ListView.GRAVITY_BOTTOM: 180 layoutParameter.setGravity(ccui.LinearLayoutParameter.BOTTOM); 181 break; 182 case ccui.ListView.GRAVITY_CENTER_VERTICAL: 183 layoutParameter.setGravity(ccui.LinearLayoutParameter.CENTER_VERTICAL); 184 break; 185 default: 186 break; 187 } 188 if (0 === itemIndex) 189 layoutParameter.setMargin(ccui.MarginZero()); 190 else 191 layoutParameter.setMargin(new ccui.Margin(this._itemsMargin, 0.0, 0.0, 0.0)); 192 }, 193 194 /** 195 * Push back a default item(create by a cloned model) into ListView. 196 */ 197 pushBackDefaultItem: function () { 198 if (this._model == null) 199 return; 200 var newItem = this._model.clone(); 201 this._remedyLayoutParameter(newItem); 202 this.addChild(newItem); 203 this._refreshViewDirty = true; 204 }, 205 206 /** 207 * Insert a default item(create by a cloned model) into ListView. 208 * @param {Number} index 209 */ 210 insertDefaultItem: function (index) { 211 if (this._model == null) 212 return; 213 var newItem = this._model.clone(); 214 this._items.splice(index, 0, newItem); 215 ccui.ScrollView.prototype.addChild.call(this, newItem); 216 this._remedyLayoutParameter(newItem); 217 218 this._refreshViewDirty = true; 219 }, 220 221 /** 222 * Push back custom item into ListView. 223 * @param {ccui.Widget} item 224 */ 225 pushBackCustomItem: function (item) { 226 this._remedyLayoutParameter(item); 227 this.addChild(item); 228 this._refreshViewDirty = true; 229 }, 230 231 /** 232 * add child to ListView 233 * @override 234 * @param {cc.Node} widget 235 * @param {Number} [zOrder] 236 * @param {Number|String} [tag] tag or name 237 */ 238 addChild: function (widget, zOrder, tag) { 239 if (widget) { 240 zOrder = zOrder || widget.getLocalZOrder(); 241 tag = tag || widget.getName(); 242 ccui.ScrollView.prototype.addChild.call(this, widget, zOrder, tag); 243 if(widget instanceof ccui.Widget) 244 { 245 this._items.push(widget); 246 this._onItemListChanged(); 247 } 248 } 249 }, 250 251 /** 252 * remove child from ListView 253 * @override 254 * @param {cc.Node} widget 255 * @param {Boolean} [cleanup=true] 256 */ 257 removeChild: function(widget, cleanup){ 258 if (widget) { 259 var index = this._items.indexOf(widget); 260 if(index > -1) 261 this._items.splice(index, 1); 262 263 this._onItemListChanged(); 264 265 ccui.ScrollView.prototype.removeChild.call(this, widget, cleanup); 266 } 267 }, 268 269 /** 270 * Removes all children from ccui.ListView. 271 */ 272 removeAllChildren: function(){ 273 this.removeAllChildrenWithCleanup(true); 274 }, 275 276 /** 277 * Removes all children from ccui.ListView and do a cleanup all running actions depending on the cleanup parameter. 278 * @param {Boolean} cleanup 279 */ 280 removeAllChildrenWithCleanup: function(cleanup){ 281 ccui.ScrollView.prototype.removeAllChildrenWithCleanup.call(this, cleanup); 282 this._items = []; 283 284 this._onItemListChanged(); 285 }, 286 287 /** 288 * Push back custom item into ccui.ListView. 289 * @param {ccui.Widget} item 290 * @param {Number} index 291 */ 292 insertCustomItem: function (item, index) { 293 this._items.splice(index, 0, item); 294 295 this._onItemListChanged(); 296 ccui.ScrollView.prototype.addChild.call(this, item); 297 this._remedyLayoutParameter(item); 298 this._refreshViewDirty = true; 299 }, 300 301 /** 302 * Removes a item whose index is same as the parameter. 303 * @param {Number} index 304 */ 305 removeItem: function (index) { 306 var item = this.getItem(index); 307 if (item == null) 308 return; 309 this.removeChild(item, true); 310 this._refreshViewDirty = true; 311 }, 312 313 /** 314 * Removes the last item of ccui.ListView. 315 */ 316 removeLastItem: function () { 317 this.removeItem(this._items.length - 1); 318 }, 319 320 /** 321 * Removes all items from ccui.ListView. 322 */ 323 removeAllItems: function(){ 324 this.removeAllChildren(); 325 }, 326 327 /** 328 * Returns a item whose index is same as the parameter. 329 * @param {Number} index 330 * @returns {ccui.Widget} 331 */ 332 getItem: function (index) { 333 if (index < 0 || index >= this._items.length) 334 return null; 335 return this._items[index]; 336 }, 337 338 /** 339 * Returns the item container. 340 * @returns {Array} 341 */ 342 getItems: function () { 343 return this._items; 344 }, 345 346 /** 347 * Returns the index of item. 348 * @param {ccui.Widget} item the item which need to be checked. 349 * @returns {Number} the index of item. 350 */ 351 getIndex: function (item) { 352 if(item == null) 353 return -1; 354 return this._items.indexOf(item); 355 }, 356 357 /** 358 * Changes the gravity of ListView. 359 * @param {ccui.ListView.GRAVITY_LEFT|ccui.ListView.GRAVITY_RIGHT|ccui.ListView.GRAVITY_CENTER_HORIZONTAL|ccui.ListView.GRAVITY_BOTTOM|ccui.ListView.GRAVITY_CENTER_VERTICAL} gravity 360 */ 361 setGravity: function (gravity) { 362 if (this._gravity === gravity) 363 return; 364 this._gravity = gravity; 365 this._refreshViewDirty = true; 366 }, 367 368 /** 369 * Set magnetic type of ListView. 370 * @param {ccui.ListView.MAGNETIC_NONE|ccui.ListView.MAGNETIC_CENTER,ccui.ListView.MAGNETIC_BOTH_END|ccui.ListView.MAGNETIC_LEFT|ccui.ListView.MAGNETIC_RIGHT|ccui.ListView.MAGNETIC_TOP|ccui.ListView.MAGNETIC_BOTTOM} magneticType 371 */ 372 setMagneticType: function(magneticType) 373 { 374 this._magneticType = magneticType; 375 this._onItemListChanged(); 376 this._startMagneticScroll(); 377 }, 378 379 /** 380 * Get magnetic type of ListView. 381 * @returns {number} 382 */ 383 getMagneticType: function() 384 { 385 return this._magneticType; 386 }, 387 388 /** 389 * Set magnetic allowed out of boundary. 390 * @param {boolean} magneticAllowedOutOfBoundary 391 */ 392 setMagneticAllowedOutOfBoundary: function(magneticAllowedOutOfBoundary) 393 { 394 this._magneticAllowedOutOfBoundary = magneticAllowedOutOfBoundary; 395 }, 396 397 /** 398 * Query whether the magnetic out of boundary is allowed. 399 * @returns {boolean} 400 */ 401 getMagneticAllowedOutOfBoundary: function() 402 { 403 return this._magneticAllowedOutOfBoundary; 404 }, 405 406 /** 407 * Changes the margin between each item. 408 * @param {Number} margin 409 */ 410 setItemsMargin: function (margin) { 411 if (this._itemsMargin === margin) 412 return; 413 this._itemsMargin = margin; 414 this._refreshViewDirty = true; 415 }, 416 417 /** 418 * Returns the margin between each item. 419 * @returns {Number} 420 */ 421 getItemsMargin:function(){ 422 return this._itemsMargin; 423 }, 424 425 /** 426 * Changes scroll direction of ccui.ListView. 427 * @param {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} dir 428 */ 429 setDirection: function (dir) { 430 switch (dir) { 431 case ccui.ScrollView.DIR_VERTICAL: 432 this.setLayoutType(ccui.Layout.LINEAR_VERTICAL); 433 break; 434 case ccui.ScrollView.DIR_HORIZONTAL: 435 this.setLayoutType(ccui.Layout.LINEAR_HORIZONTAL); 436 break; 437 case ccui.ScrollView.DIR_BOTH: 438 return; 439 default: 440 return; 441 break; 442 } 443 ccui.ScrollView.prototype.setDirection.call(this, dir); 444 }, 445 446 _getHowMuchOutOfBoundary: function(addition) 447 { 448 if(addition === undefined) 449 addition = cc.p(0, 0); 450 451 if(!this._magneticAllowedOutOfBoundary || this._items.length === 0) 452 { 453 return ccui.ScrollView.prototype._getHowMuchOutOfBoundary.call(this, addition); 454 } 455 else if(this._magneticType === ccui.ListView.MAGNETIC_NONE || this._magneticType === ccui.ListView.MAGNETIC_BOTH_END) 456 { 457 return ccui.ScrollView.prototype._getHowMuchOutOfBoundary.call(this, addition); 458 } 459 else if(addition.x === 0 && addition.y === 0 && !this._outOfBoundaryAmountDirty) 460 { 461 return this._outOfBoundaryAmount; 462 } 463 464 // If it is allowed to be out of boundary by magnetic, adjust the boundaries according to the magnetic type. 465 var leftBoundary = this._leftBoundary; 466 var rightBoundary = this._rightBoundary; 467 var topBoundary = this._topBoundary; 468 var bottomBoundary = this._bottomBoundary; 469 470 var lastItemIndex = this._items.length - 1; 471 var contentSize = this.getContentSize(); 472 var firstItemAdjustment = cc.p(0, 0); 473 var lastItemAdjustment = cc.p(0, 0); 474 475 switch (this._magneticType) 476 { 477 case ccui.ListView.MAGNETIC_CENTER: 478 firstItemAdjustment.x = (contentSize.width - this._items[0].width) / 2; 479 firstItemAdjustment.y = (contentSize.height - this._items[0].height) / 2; 480 481 lastItemAdjustment.x = (contentSize.width - this._items[lastItemIndex].width) / 2; 482 lastItemAdjustment.y = (contentSize.height - this._items[lastItemIndex].height) / 2; 483 484 break; 485 case ccui.ListView.MAGNETIC_LEFT: 486 case ccui.ListView.MAGNETIC_TOP: 487 lastItemAdjustment.x = contentSize.width - this._items[lastItemIndex].width; 488 lastItemAdjustment.y = contentSize.height - this._items[lastItemIndex].height; 489 break; 490 case ccui.ListView.MAGNETIC_RIGHT: 491 case ccui.ListView.MAGNETIC_BOTTOM: 492 firstItemAdjustment.x = contentSize.width - this._items[0].width; 493 firstItemAdjustment.y = contentSize.height - this._items[0].height; 494 break; 495 } 496 497 leftBoundary += firstItemAdjustment.x; 498 rightBoundary -= lastItemAdjustment.x; 499 topBoundary -= firstItemAdjustment.y; 500 bottomBoundary += lastItemAdjustment.y; 501 502 503 // Calculate the actual amount 504 var outOfBoundaryAmount = cc.p(0, 0); 505 506 if(this._innerContainer.getLeftBoundary() + addition.x > leftBoundary) 507 { 508 outOfBoundaryAmount.x = leftBoundary - (this._innerContainer.getLeftBoundary() + addition.x); 509 } 510 else if(this._innerContainer.getRightBoundary() + addition.x < rightBoundary) 511 { 512 outOfBoundaryAmount.x = rightBoundary - (this._innerContainer.getRightBoundary() + addition.x); 513 } 514 515 if(this._innerContainer.getTopBoundary() + addition.y < topBoundary) 516 { 517 outOfBoundaryAmount.y = topBoundary - (this._innerContainer.getTopBoundary() + addition.y); 518 } 519 else if(this._innerContainer.getBottomBoundary() + addition.y > bottomBoundary) 520 { 521 outOfBoundaryAmount.y = bottomBoundary - (this._innerContainer.getBottomBoundary() + addition.y); 522 } 523 524 if(addition.x === 0 && addition.y === 0) 525 { 526 this._outOfBoundaryAmount = outOfBoundaryAmount; 527 this._outOfBoundaryAmountDirty = false; 528 } 529 return outOfBoundaryAmount; 530 }, 531 532 _calculateItemPositionWithAnchor: function(item, itemAnchorPoint) 533 { 534 var origin = cc.p(item.getLeftBoundary(), item.getBottomBoundary()); 535 var size = item.getContentSize(); 536 537 return cc.p(origin. x + size.width * itemAnchorPoint.x, origin.y + size.height * itemAnchorPoint.y); 538 }, 539 540 _findClosestItem: function(targetPosition, items, itemAnchorPoint, firstIndex, distanceFromFirst, lastIndex, distanceFromLast) 541 { 542 cc.assert(firstIndex >= 0 && lastIndex < items.length && firstIndex <= lastIndex, ""); 543 if (firstIndex === lastIndex) 544 { 545 return items[firstIndex]; 546 } 547 if (lastIndex - firstIndex === 1) 548 { 549 if (distanceFromFirst <= distanceFromLast) 550 { 551 return items[firstIndex]; 552 } 553 else 554 { 555 return items[lastIndex]; 556 } 557 } 558 559 // Binary search 560 var midIndex = Math.floor((firstIndex + lastIndex) / 2); 561 var itemPosition = this._calculateItemPositionWithAnchor(items[midIndex], itemAnchorPoint); 562 var distanceFromMid = cc.pLength(cc.pSub(targetPosition, itemPosition)); 563 564 if (distanceFromFirst <= distanceFromLast) 565 { 566 // Left half 567 return this._findClosestItem(targetPosition, items, itemAnchorPoint, firstIndex, distanceFromFirst, midIndex, distanceFromMid); 568 } 569 else 570 { 571 // Right half 572 return this._findClosestItem(targetPosition, items, itemAnchorPoint, midIndex, distanceFromMid, lastIndex, distanceFromLast); 573 } 574 }, 575 576 /** 577 * Query the closest item to a specific position in inner container. 578 * 579 * @param {cc.Point} targetPosition Specifies the target position in inner container's coordinates. 580 * @param {cc.Point} itemAnchorPoint Specifies an anchor point of each item for position to calculate distance. 581 * @returns {?ccui.Widget} A item instance if list view is not empty. Otherwise, returns null. 582 */ 583 getClosestItemToPosition: function(targetPosition, itemAnchorPoint) 584 { 585 if (this._items.length === 0) 586 { 587 return null; 588 } 589 590 // Find the closest item through binary search 591 var firstIndex = 0; 592 var firstPosition = this._calculateItemPositionWithAnchor(this._items[firstIndex], itemAnchorPoint); 593 var distanceFromFirst = cc.pLength(cc.pSub(targetPosition, firstPosition)); 594 595 var lastIndex = this._items.length - 1; 596 var lastPosition = this._calculateItemPositionWithAnchor(this._items[lastIndex], itemAnchorPoint); 597 var distanceFromLast = cc.pLength(cc.pSub(targetPosition, lastPosition)); 598 599 return this._findClosestItem(targetPosition, this._items, itemAnchorPoint, firstIndex, distanceFromFirst, lastIndex, distanceFromLast); 600 }, 601 602 /** 603 * Query the closest item to a specific position in current view.<br/> 604 * For instance, to find the item in the center of view, call 'getClosestItemToPositionInCurrentView(cc.p(0.5, 0.5), cc.p(0.5, 0.5))'. 605 * 606 * @param {cc.Point} positionRatioInView Specifies the target position with ratio in list view's content size. 607 * @param {cc.Point} itemAnchorPoint Specifies an anchor point of each item for position to calculate distance. 608 * @returns {?ccui.Widget} A item instance if list view is not empty. Otherwise, returns null. 609 */ 610 611 getClosestItemToPositionInCurrentView: function(positionRatioInView, itemAnchorPoint) 612 { 613 // Calculate the target position 614 var contentSize = this.getContentSize(); 615 var targetPosition = cc.pMult(this._innerContainer.getPosition(), -1); 616 targetPosition.x += contentSize.width * positionRatioInView.x; 617 targetPosition.y += contentSize.height * positionRatioInView.y; 618 619 return this.getClosestItemToPosition(targetPosition, itemAnchorPoint); 620 }, 621 622 /** 623 * Query the center item 624 * @returns {?ccui.Widget} A item instance. 625 */ 626 getCenterItemInCurrentView: function() 627 { 628 return this.getClosestItemToPositionInCurrentView(cc.p(0.5, 0.5), cc.p(0.5, 0.5)); 629 }, 630 631 /** 632 * Query the leftmost item in horizontal list 633 * @returns {?ccui.Widget} A item instance. 634 */ 635 getLeftmostItemInCurrentView: function() 636 { 637 if(this._direction === ccui.ScrollView.DIR_HORIZONTAL) 638 { 639 return this.getClosestItemToPositionInCurrentView(cc.p(0, 0.5), cc.p(0.5, 0.5)); 640 } 641 642 return null; 643 }, 644 645 /** 646 * Query the rightmost item in horizontal list 647 * @returns {?ccui.Widget} A item instance. 648 */ 649 getRightmostItemInCurrentView: function() 650 { 651 if(this._direction === ccui.ScrollView.DIR_HORIZONTAL) 652 { 653 return this.getClosestItemToPositionInCurrentView(cc.p(1, 0.5), cc.p(0.5, 0.5)); 654 } 655 656 return null; 657 }, 658 659 /** 660 * Query the topmost item in horizontal list 661 * @returns {?ccui.Widget} A item instance. 662 */ 663 getTopmostItemInCurrentView: function() 664 { 665 if(this._direction === ccui.ScrollView.DIR_VERTICAL) 666 { 667 return this.getClosestItemToPositionInCurrentView(cc.p(0.5, 1), cc.p(0.5, 0.5)); 668 } 669 670 return null; 671 }, 672 673 /** 674 * Query the topmost item in horizontal list 675 * @returns {?ccui.Widget} A item instance. 676 */ 677 getBottommostItemInCurrentView: function() 678 { 679 if(this._direction === ccui.ScrollView.DIR_VERTICAL) 680 { 681 return this.getClosestItemToPositionInCurrentView(cc.p(0.5, 0), cc.p(0.5, 0.5)); 682 } 683 684 return null; 685 }, 686 687 _calculateItemDestination: function(positionRatioInView, item, itemAnchorPoint) 688 { 689 var contentSize = this.getContentSize(); 690 var positionInView = cc.p(0, 0); 691 positionInView.x += contentSize.width * positionRatioInView.x; 692 positionInView.y += contentSize.height * positionRatioInView.y; 693 694 var itemPosition = this._calculateItemPositionWithAnchor(item, itemAnchorPoint); 695 return cc.pMult(cc.pSub(itemPosition, positionInView), -1); 696 }, 697 698 jumpToBottom: function() 699 { 700 this.doLayout(); 701 ccui.ScrollView.prototype.jumpToBottom.call(this); 702 }, 703 704 jumpToTop: function() 705 { 706 this.doLayout(); 707 ccui.ScrollView.prototype.jumpToTop.call(this); 708 }, 709 710 jumpToLeft: function() 711 { 712 this.doLayout(); 713 ccui.ScrollView.prototype.jumpToLeft.call(this); 714 }, 715 716 jumpToRight: function() 717 { 718 this.doLayout(); 719 ccui.ScrollView.prototype.jumpToRight.call(this); 720 }, 721 722 jumpToTopLeft: function() 723 { 724 this.doLayout(); 725 ccui.ScrollView.prototype.jumpToTopLeft.call(this); 726 }, 727 728 jumpToTopRight: function() 729 { 730 this.doLayout(); 731 ccui.ScrollView.prototype.jumpToTopRight.call(this); 732 }, 733 734 jumpToBottomLeft: function() 735 { 736 this.doLayout(); 737 ccui.ScrollView.prototype.jumpToBottomLeft.call(this); 738 }, 739 740 jumpToBottomRight: function() 741 { 742 this.doLayout(); 743 ccui.ScrollView.prototype.jumpToBottomRight.call(this); 744 }, 745 746 jumpToPercentVertical: function(percent) 747 { 748 this.doLayout(); 749 ccui.ScrollView.prototype.jumpToPercentVertical.call(this, percent); 750 }, 751 752 jumpToPercentHorizontal: function(percent) 753 { 754 this.doLayout(); 755 ccui.ScrollView.prototype.jumpToPercentHorizontal.call(this, percent); 756 }, 757 758 jumpToPercentBothDirection: function(percent) 759 { 760 this.doLayout(); 761 ccui.ScrollView.prototype.jumpToPercentBothDirection.call(this, percent); 762 }, 763 764 /** 765 * Jump to specific item 766 * @param {number} itemIndex Specifies the item's index 767 * @param {cc.Point} positionRatioInView Specifies the position with ratio in list view's content size. 768 * @param {cc.Point} itemAnchorPoint Specifies an anchor point of each item for position to calculate distance. 769 */ 770 jumpToItem: function(itemIndex, positionRatioInView, itemAnchorPoint) 771 { 772 var item = this.getItem(itemIndex); 773 774 if(!item) 775 return; 776 777 this.doLayout(); 778 779 var destination = this._calculateItemDestination(positionRatioInView, item, itemAnchorPoint); 780 781 if(!this.bounceEnabled) 782 { 783 var delta = cc.pSub(destination, this._innerContainer.getPosition()); 784 var outOfBoundary = this._getHowMuchOutOfBoundary(delta); 785 destination.x += outOfBoundary.x; 786 destination.y += outOfBoundary.y; 787 } 788 789 this._jumpToDestination(destination); 790 }, 791 792 /** 793 * Scroll to specific item 794 * @param {number} itemIndex Specifies the item's index 795 * @param {cc.Point} positionRatioInView Specifies the position with ratio in list view's content size. 796 * @param {cc.Point} itemAnchorPoint Specifies an anchor point of each item for position to calculate distance. 797 * @param {number} [timeInSec = 1.0] Scroll time 798 */ 799 scrollToItem: function(itemIndex, positionRatioInView, itemAnchorPoint, timeInSec) 800 { 801 if(timeInSec === undefined) 802 timeInSec = 1; 803 804 var item = this.getItem(itemIndex); 805 806 if(!item) 807 return; 808 809 var destination = this._calculateItemDestination(positionRatioInView, item, itemAnchorPoint); 810 this._startAutoScrollToDestination(destination, timeInSec, true); 811 }, 812 813 /** 814 * Requests refresh list view. 815 * @deprecated Use method requestDoLayout() instead 816 */ 817 requestRefreshView: function () { 818 this._refreshViewDirty = true; 819 }, 820 821 /** 822 * Refreshes list view. 823 * @deprecated Use method forceDoLayout() instead 824 */ 825 refreshView: function () { 826 this.forceDoLayout() 827 }, 828 829 /** 830 * provides a public _doLayout function for Editor. it calls _doLayout. 831 */ 832 doLayout: function(){ 833 this._doLayout(); 834 }, 835 836 requestDoLayout: function() 837 { 838 this._refreshViewDirty = true; 839 }, 840 841 _doLayout: function(){ 842 //ccui.Layout.prototype._doLayout.call(this); 843 if (this._refreshViewDirty) { 844 var locItems = this._items; 845 for (var i = 0; i < locItems.length; i++) { 846 var item = locItems[i]; 847 item.setLocalZOrder(i); 848 this._remedyLayoutParameter(item); 849 } 850 this._updateInnerContainerSize(); 851 this._innerContainer.forceDoLayout(); 852 this._refreshViewDirty = false; 853 } 854 }, 855 856 /** 857 * Adds event listener to ccui.ListView. 858 * @param {Function} selector 859 * @param {Object} [target=] 860 * @deprecated since v3.0, please use addEventListener instead. 861 */ 862 addEventListenerListView: function (selector, target) { 863 this._listViewEventListener = target; 864 this._listViewEventSelector = selector; 865 }, 866 867 _selectedItemEvent: function (event) { 868 var eventEnum = (event === ccui.Widget.TOUCH_BEGAN) ? ccui.ListView.ON_SELECTED_ITEM_START : ccui.ListView.ON_SELECTED_ITEM_END; 869 if(this._listViewEventSelector){ 870 if (this._listViewEventListener) 871 this._listViewEventSelector.call(this._listViewEventListener, this, eventEnum); 872 else 873 this._listViewEventSelector(this, eventEnum); 874 } 875 if(this._ccEventCallback) 876 this._ccEventCallback(this, eventEnum); 877 }, 878 879 /** 880 * Intercept touch event, handle its child's touch event. 881 * @param {Number} eventType 882 * @param {ccui.Widget} sender 883 * @param {cc.Touch} touch 884 */ 885 interceptTouchEvent: function (eventType, sender, touch) { 886 ccui.ScrollView.prototype.interceptTouchEvent.call(this, eventType, sender, touch); 887 if(!this._touchEnabled) 888 { 889 return; 890 } 891 if (eventType !== ccui.Widget.TOUCH_MOVED) { 892 var parent = sender; 893 while (parent) { 894 if (parent && parent.getParent() === this._innerContainer) { 895 this._curSelectedIndex = this.getIndex(parent); 896 break; 897 } 898 parent = parent.getParent(); 899 } 900 if (sender.isHighlighted()) 901 this._selectedItemEvent(eventType); 902 } 903 }, 904 905 /** 906 * Returns current selected index 907 * @returns {number} 908 */ 909 getCurSelectedIndex: function () { 910 return this._curSelectedIndex; 911 }, 912 913 _onSizeChanged: function () { 914 ccui.ScrollView.prototype._onSizeChanged.call(this); 915 this._refreshViewDirty = true; 916 }, 917 918 /** 919 * Returns the "class name" of ccui.ListView. 920 * @returns {string} 921 */ 922 getDescription: function () { 923 return "ListView"; 924 }, 925 926 _createCloneInstance: function () { 927 return new ccui.ListView(); 928 }, 929 930 _copyClonedWidgetChildren: function (model) { 931 var arrayItems = model.getItems(); 932 for (var i = 0; i < arrayItems.length; i++) { 933 var item = arrayItems[i]; 934 this.pushBackCustomItem(item.clone()); 935 } 936 }, 937 938 _copySpecialProperties: function (listView) { 939 if(listView instanceof ccui.ListView){ 940 ccui.ScrollView.prototype._copySpecialProperties.call(this, listView); 941 this.setItemModel(listView._model); 942 this.setItemsMargin(listView._itemsMargin); 943 this.setGravity(listView._gravity); 944 945 this._listViewEventListener = listView._listViewEventListener; 946 this._listViewEventSelector = listView._listViewEventSelector; 947 } 948 }, 949 950 _startAttenuatingAutoScroll: function(deltaMove, initialVelocity) 951 { 952 var adjustedDeltaMove = deltaMove; 953 954 if(this._items.length !== 0 && this._magneticType !== ccui.ListView.MAGNETIC_NONE) 955 { 956 adjustedDeltaMove = this._flattenVectorByDirection(adjustedDeltaMove); 957 958 var howMuchOutOfBoundary = this._getHowMuchOutOfBoundary(adjustedDeltaMove); 959 // If the destination is out of boundary, do nothing here. Because it will be handled by bouncing back. 960 if(howMuchOutOfBoundary.x === 0 && howMuchOutOfBoundary.y === 0 ) 961 { 962 var magType = this._magneticType; 963 if(magType === ccui.ListView.MAGNETIC_BOTH_END) 964 { 965 if(this._direction === ccui.ScrollView.DIR_HORIZONTAL) 966 { 967 magType = (adjustedDeltaMove.x > 0 ? ccui.ListView.MAGNETIC_LEFT : ccui.ListView.MAGNETIC_RIGHT); 968 } 969 else if(this._direction === ccui.ScrollView.DIR_VERTICAL) 970 { 971 magType = (adjustedDeltaMove.y > 0 ? ccui.ListView.MAGNETIC_BOTTOM : ccui.ListView.MAGNETIC_TOP); 972 } 973 } 974 975 // Adjust the delta move amount according to the magnetic type 976 var magneticAnchorPoint = this._getAnchorPointByMagneticType(magType); 977 var magneticPosition = cc.pMult(this._innerContainer.getPosition(), -1); 978 magneticPosition.x += this.width * magneticAnchorPoint.x; 979 magneticPosition.y += this.height * magneticAnchorPoint.y; 980 981 var pTargetItem = this.getClosestItemToPosition(cc.pSub(magneticPosition, adjustedDeltaMove), magneticAnchorPoint); 982 var itemPosition = this._calculateItemPositionWithAnchor(pTargetItem, magneticAnchorPoint); 983 adjustedDeltaMove = cc.pSub(magneticPosition, itemPosition); 984 } 985 } 986 ccui.ScrollView.prototype._startAttenuatingAutoScroll.call(this,adjustedDeltaMove, initialVelocity); 987 }, 988 989 _getAnchorPointByMagneticType: function(magneticType) 990 { 991 switch(magneticType) 992 { 993 case ccui.ListView.MAGNETIC_NONE: return cc.p(0, 0); 994 case ccui.ListView.MAGNETIC_BOTH_END: return cc.p(0, 1); 995 case ccui.ListView.MAGNETIC_CENTER: return cc.p(0.5, 0.5); 996 case ccui.ListView.MAGNETIC_LEFT: return cc.p(0, 0.5); 997 case ccui.ListView.MAGNETIC_RIGHT: return cc.p(1, 0.5); 998 case ccui.ListView.MAGNETIC_TOP: return cc.p(0.5, 1); 999 case ccui.ListView.MAGNETIC_BOTTOM: return cc.p(0.5, 0); 1000 } 1001 1002 return cc.p(0, 0); 1003 }, 1004 1005 _startMagneticScroll: function() 1006 { 1007 if(this._items.length === 0 || this._magneticType === ccui.ListView.MAGNETIC_NONE) 1008 { 1009 return; 1010 } 1011 1012 // Find the closest item 1013 var magneticAnchorPoint =this._getAnchorPointByMagneticType(this._magneticType); 1014 var magneticPosition = cc.pMult(this._innerContainer.getPosition(), -1); 1015 magneticPosition.x += this.width * magneticAnchorPoint.x; 1016 magneticPosition.y += this.height * magneticAnchorPoint.y; 1017 1018 var pTargetItem = this.getClosestItemToPosition(magneticPosition, magneticAnchorPoint); 1019 this.scrollToItem(this.getIndex(pTargetItem), magneticAnchorPoint, magneticAnchorPoint); 1020 } 1021 }); 1022 1023 /** 1024 * allocates and initializes a UIListView. 1025 * @deprecated since v3.0, please use new ccui.ListView() instead. 1026 */ 1027 ccui.ListView.create = function () { 1028 return new ccui.ListView(); 1029 }; 1030 1031 // Constants 1032 //listView event type 1033 /** 1034 * The flag selected item of ccui.ListView's event. 1035 * @constant 1036 * @type {number} 1037 */ 1038 ccui.ListView.EVENT_SELECTED_ITEM = 0; 1039 1040 /** 1041 * The flag selected item start of ccui.ListView's event. 1042 * @constant 1043 * @type {number} 1044 */ 1045 ccui.ListView.ON_SELECTED_ITEM_START = 0; 1046 /** 1047 * The flag selected item end of ccui.ListView's event. 1048 * @constant 1049 * @type {number} 1050 */ 1051 ccui.ListView.ON_SELECTED_ITEM_END = 1; 1052 1053 //listView gravity 1054 /** 1055 * The left flag of ccui.ListView's gravity. 1056 * @constant 1057 * @type {number} 1058 */ 1059 ccui.ListView.GRAVITY_LEFT = 0; 1060 /** 1061 * The right flag of ccui.ListView's gravity. 1062 * @constant 1063 * @type {number} 1064 */ 1065 ccui.ListView.GRAVITY_RIGHT = 1; 1066 /** 1067 * The center horizontal flag of ccui.ListView's gravity. 1068 * @constant 1069 * @type {number} 1070 */ 1071 ccui.ListView.GRAVITY_CENTER_HORIZONTAL = 2; 1072 /** 1073 * The top flag of ccui.ListView's gravity. 1074 * @constant 1075 * @type {number} 1076 */ 1077 ccui.ListView.GRAVITY_TOP = 3; 1078 /** 1079 * The bottom flag of ccui.ListView's gravity. 1080 * @constant 1081 * @type {number} 1082 */ 1083 ccui.ListView.GRAVITY_BOTTOM = 4; 1084 /** 1085 * The center vertical flag of ccui.ListView's gravity. 1086 * @constant 1087 * @type {number} 1088 */ 1089 ccui.ListView.GRAVITY_CENTER_VERTICAL = 5; 1090 1091 /** 1092 * The flag of ccui.ListView's magnetic none type. 1093 * @constant 1094 * @type {number} 1095 */ 1096 ccui.ListView.MAGNETIC_NONE = 0; 1097 /** 1098 * The flag of ccui.ListView's magnetic center type.<br/> 1099 * ListView tries to align its items in center of current view. 1100 * @constant 1101 * @type {number} 1102 */ 1103 ccui.ListView.MAGNETIC_CENTER = 1; 1104 /** 1105 * The flag of ccui.ListView's magnetic both end type.<br/> 1106 * ListView tries to align its items in left or right end if it is horizontal, top or bottom in vertical. <br/> 1107 * The aligning side (left or right, top or bottom) is determined by user's scroll direction. 1108 * @constant 1109 * @type {number} 1110 */ 1111 ccui.ListView.MAGNETIC_BOTH_END = 2; 1112 /** 1113 * The flag of ccui.ListView's magnetic left type. 1114 * @constant 1115 * @type {number} 1116 */ 1117 ccui.ListView.MAGNETIC_LEFT = 3; 1118 /** 1119 * The flag of ccui.ListView's magnetic right type. 1120 * @constant 1121 * @type {number} 1122 */ 1123 ccui.ListView.MAGNETIC_RIGHT = 4; 1124 /** 1125 * The flag of ccui.ListView's magnetic top type. 1126 * @constant 1127 * @type {number} 1128 */ 1129 ccui.ListView.MAGNETIC_TOP = 5; 1130 /** 1131 * The flag of ccui.ListView's magnetic bottom type. 1132 * @constant 1133 * @type {number} 1134 */ 1135 ccui.ListView.MAGNETIC_BOTTOM = 6;