1. 程式人生 > >CocosCreate之ScrollView詳解

CocosCreate之ScrollView詳解

ram 初始化 to do 計算 講解 timer space 無限 text

  ScrollView是比較常用的UI組件之一,遊戲中的任務榜、排行榜都少不了它,實際使用中存在一個問題,例如:在排行榜中要顯示前100名玩家,如果真的把這100名玩家的信息全部創建,並加載進ScrollView,對移動設備的寶貴內存會是巨大的浪費。其實玩家在屏幕上總能看到的最多只有7、8項而已,所以實際上只用創建比顯示多一點的數量,再通過緩沖區實時動態更新機制,就可以給玩家呈現出盡可能多數量的列表(依內存而定,理論上無限),即節省內存,也不影響性能。

在CocosCreate官方提供的example中,有個ScrollView的例子,就使用了動態更新機制,我把它摘出來做為一個單獨的工程,加上中文註釋,並對代碼稍做改動,運行截圖如下所示:

技術分享

  可以看到,列表中顯示一共有100行,但一屏最多展示7.5行,而實際上在內存中只真正創建了15項,所有看到的這100行,都是在上下滾動事件中,通過動態更新這15項的坐標和內容來實現的。要了解它的運行原理,先看下圖:

技術分享

  如圖把ScrollView分成三部分,按區域從小到大依次是:

1、屏幕可見區。指屏幕上玩家可看可操作的列表區域,在此demo中有7.5行;

2、緩沖區。指內存中真正創建了的列表所占的區域,在此demo中有15行;

3、content區。指整個ScrollView要顯示的區域,在此demo中有100行;

  下面再分三種情況講解:

1、剛初始化完成時:此時在右側按鈕上提示有100行,實際上只創建了第1-15行,而玩家能看到的是第1-7行。如果玩家想要看到更多,必然會向上或向下滾動屏幕;

2、向上滾動時:在移動設備上,如果玩家想要看到下面的行,所做的操作是觸摸往上滑動,則整個content區往上移動,也帶動content區的item往上移動,update函數會不斷遍歷所創建的15項item,如果檢測到某item的y坐標超出了緩沖區的上邊界,則把該item往下移動一個緩沖區的高度,並更新它的顯示ID;

3、向下滾動時:同理,content區的item往下移動,update不斷遍歷所創建的15項item,如果檢測到某item的y坐標越過了緩沖區的下邊界,則把該item往上移動一個緩沖區的高度,並更新它的顯示ID;

  關鍵代碼如下所示:

    // 返回item在ScrollView空間的坐標值
getPositionInView: function (item) { let worldPos = item.parent.convertToWorldSpaceAR(item.position); let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos); return viewPos; }, // 每幀調用一次。根據滾動位置動態更新item的坐標和顯示(所以spawnCount可以比totalCount少很多) update: function(dt) { this.updateTimer += dt; if (this.updateTimer < this.updateInterval) { return; // we don‘t need to do the math every frame } this.updateTimer = 0; let items = this.items; // 如果當前content的y坐標小於上次記錄值,則代表往下滾動,否則往上。 let isDown = this.scrollView.content.y < this.lastContentPosY; // 實際創建項占了多高(即它們的高度累加) let offset = (this.itemTemplate.height + this.spacing) * items.length; let newY = 0; // 遍歷數組,更新item的位置和顯示 for (let i = 0; i < items.length; ++i) { let viewPos = this.getPositionInView(items[i]); if (isDown) { // 提前計算出該item的新的y坐標 newY = items[i].y + offset; // 如果往下滾動時item已經超出緩沖矩形,且newY未超出content上邊界, // 則更新item的坐標(即上移了一個offset的位置),同時更新item的顯示內容 if (viewPos.y < -this.bufferZone && newY < 0) { items[i].setPositionY(newY); let item = items[i].getComponent(‘Item‘); let itemId = item.itemID - items.length; // update item id item.updateItem(i, itemId); } } else { // 提前計算出該item的新的y坐標 newY = items[i].y - offset; // 如果往上滾動時item已經超出緩沖矩形,且newY未超出content下邊界, // 則更新item的坐標(即下移了一個offset的位置),同時更新item的顯示內容 if (viewPos.y > this.bufferZone && newY > -this.content.height) { items[i].setPositionY(newY); let item = items[i].getComponent(‘Item‘); let itemId = item.itemID + items.length; item.updateItem(i, itemId); } } } // 更新lastContentPosY和總項數顯示 this.lastContentPosY = this.scrollView.content.y; this.lblTotalItems.string = "Total Items: " + this.totalCount; },

  在此demo中,ScrollView列表顯示的item其實是個按鈕,而它做為預制資源,其實可以在Creator中編輯成各種UI,並不局限於按鈕形式。

  最後,附上該Creator工程完整源代碼的github地址:https://github.com/foupwang/CocosCreatorScrollViewDemo.git

CocosCreate之ScrollView詳解