應用場景
對於背包界面,排行榜列表,聊天消息,等有大量的UI列表的界面,常規做法是為每一條數據生成一個格子,在數據量越大的情況下,會生成越來越多的Gameobject,引起卡頓。
這篇文章講述的就是解決UI列表卡頓的方法,在列表中只生成指定數量的Gameobject,滑動時進行數據更新,保證性能。

LoopScrollRect(UGUI循環列表不卡頓)
插件地址:https://github.com/qiankanglai/LoopScrollRect
中文文檔:http://qiankanglai.me/2015/08/15/LoopScrollRect/
適用於UGUI,支持UGUI原生的GridLayout,ScrollBar
我的修改版本:https://github.com/zhaoqingqing/LoopScrollRect.git
原理分析
在UGUI的ScrollRect基礎上作的修改
兩者的差異部分使用//==========LoopScrollRect========== 標識出來了,源代碼對比:
UGUI的ScrollRect:https://bitbucket.org/Unity-Technologies/ui/src/5.2/UnityEngine.UI/UI/Core/ScrollRect.cs?fileviewer=file-view-default
LoopScrollRect:https://github.com/qiankanglai/LoopScrollRect/blob/master/Assets/Scripts/LoopScrollRect.cs
使用示例
可以參考demo的示例
如果在lua中使用,邏輯和C#的一樣,步驟如下:
1.注冊事件、取消注冊
2.在刷新函數根據數據更新列表
3.調用邏輯
注冊事件
注冊列表滑動事件,在OnOpen中注冊,用來刷新列表
此事件在C#中觸發,Lua中注冊回調,事件有兩個參數
self.chatScrollRect.dataSource.ScrollToTextEvent = function(cellTrans, idx) self:OnScrollChat(cellTrans, idx) end
取消注冊
取消注冊的列表滑動事件,在OnClose中取消
self.chatScrollRect.dataSource.ScrollToTextEvent = nil
觸發滑動事件(刷新每一項)
在以下情況事件會被觸發:
- 如果列表的值已全部生成出來,在滑動過程中不會觸發,否則會觸發
- 調用RefreshCells或RefillCellsFromEnd時,會觸發
function UIRewardResources:OnScrollEvent(cellTrans, idx)
if (not cellTrans or not idx) then
return
end
idx = idx + 1 -- Lua的索引從1開始,而scrollRect是從0開始
local data = DataCenter.resource.data[idx]
--執行你的刷新邏輯
self:DoRenderItem(cellTrans, data)
end
手動刷新列表
--設置列表的總數,並刷新cell
self.scrollRect.itemTypeStart = 0 ---讓列表從頭開始滑動
self.chatScrollRect.totalCount = 10
self.chatScrollRect:RefreshCells()
刷新並讓列表滑動到底部
在聊天列表中每次發言完都是最新消息都在最底下,可以使用這個接口
self.chatScrollRect:RefillCellsFromEnd()
如果在模擬器上出現無法滑動到底部的現象,在Unity 2018.4.15f + 網易MuMu模擬環境下可添加下列代碼
IEnumerator RefreshToEnd()
{
yield return null;
yield return null;
Canvas.ForceUpdateCanvases();
loop_scroll.verticalNormalizedPosition = 1.0f;
}
兩個刷新函數區別
RefreshCells:列表刷新
RefillCellsFromEnd:從最底部的消息開始刷新,並滑動到底部
列表滑動到底部的事件
和UGUI的ScrollRect的做法一樣,為scrollRect添加一個scrollbar,捕捉OnEndDrag事件,示例如下:
self.chatScrollRect.onEndDrag = function(data)
if self.chatScrollbar.value >= 1 then
print("scroll to bottom")
end
end
如果你的數據量特別大,在滑動到底部事件時進行分頁請求數據
如果是做分頁:建議在滑動到某個數量級且滑動到底部時,設置一次數據,保證滑動的流暢性
prefabSource為nil
如果在熱更新的包中報prefabSource為nil,是因為熱更新dll之后,prefabSource會丟失,需要在lua對prefabSource重新賦值,示例:
self.scrollRect.prefabSource = CS.UnityEngine.UI.XLoopScrollPrefabSource(self.itemCell.gameObject)
跳轉到指定的index/Item
參考:https://github.com/qiankanglai/LoopScrollRect/issues/14
方法一:
設置itemTypeStart為需要的Pos
self.scrollRectEquipList.content.anchoredPosition = Vector2.zero
self.scrollRectEquipList.itemTypeStart = pos - 1
self.scrollRectEquipList.totalCount = equipCount
self.scrollRectEquipList:RefreshCells()
方法二:不綁定initInStart腳本,並調用 RefillCells(90)
兩次調用列表滑動位置不正確
對於tab頁簽,左右切頁都使用同一個ScrollRect,在前一個滑動到底部之后,再切到下一個頁簽,會出現列表還在滑動的現象,在切換頁簽前添加以下代碼,停止上一次的滑動,並把位置進行復位
void ResetPos()
{
loop_scroll.StopAllCoroutines();
loop_scroll.StopMovement();
loop_scroll.content.anchoredPosition = Vector2.zero;
}
技巧和事項
Cell指:每一個格子,或每一項列表
為每一個Cell都綁定LayoutElement組件,並勾選Preferred Width 和Preferred Height,且給它們賦合適值,保證列表自適應。
查找某一項的取巧做法:可以用id做為Cell的名字,當在查找時,根據FindChild(id)找到這一項,進行刷新。
滑動到指定的index/Item:
