之前已經實現過:
Unity 中實現可復用的 ListView-CSDN博客文章瀏覽閱讀5.6k次,點贊2次,收藏27次。源碼已放入我的 github,地址:Unity-ListView前言實現一個列表組件,表現方面最核心的部分就是重寫布局(Layout)。對于簡單的列表,尤其是“Cell數量固定且較少、沒有超頁滾動展示”一類的需求,使用UGUI自帶的布局組件進行布局即可。分別為:水平布局組件(Horizontal Layout Group)、豎直布局組件(Vertical Layout Gro..._unity listviewhttps://blog.csdn.net/NRatel/article/details/100561203Unity 中實現可復用的 GridView-CSDN博客文章瀏覽閱讀4k次。本文介紹了如何基于Unity的UGUI系統設計一個靈活的GridView組件。作者分析了GridLayoutGroup的參數,討論了StartCorner和StartAxis的排布方式、Constraint的靈活性以及Padding與對齊方式的巧妙結合。在實現過程中,修改了ScrollRect的關聯ScrollBar和布局接口,設計了適應不同滑動方向的布局,并實現了元素復用邏輯,包括四種滑動方向的計算。此外,還探討了Content錨點、行列約束和對齊方式的調整,以提高組件的易用性。
https://blog.csdn.net/NRatel/article/details/124063559首尾無限循環 的列表,還是以?ListView 為基礎。
在此之前,先參照 GrideView 修改?ListView:
使其 繼承 UIScrollRect(原因是必須修改部分源碼)
并支持參數:
1、MovementAxis :
????????橫向滑動 或?豎向滑動。
2、StartSide :
????????橫向滑動時,可選 元素的排列方向:從左往右 或 從右往左;
????????豎向滑動時,可選 元素的排列方向:從上往下?或 從下往上。
3、ChildAligment :
????????橫向滑動時,可選 元素的上下對齊方式:居上/居中/居下;
? ? ? ? 豎向滑動時,可選 元素的左右對齊方式:居左/居中/居右。
----------------------------------------- NRatel割 -----------------------------------------
Loop 首尾循環的改動要點:
(以下僅以 MovementAxis=Horizontal,StartSide=Left 的情形闡述)
先看這種情況:核心內容寬度 > viewport 寬度
1、使 Content 在移動時,非原核心內容區也能夠顯示 0~Count-1 范圍內的 Cell元素,讓越界索引不要提前retrun,而是在顯示時轉到 0~Count-1 之中。
如圖:黑色區域為 Content, 當其繼續往右滑動時,進入Viewport、但已超出 Content 的部分,仍能生成越界元素,并能將越界索引轉到 界內索引之中。
需要修改?CalcIndexes,使其不要立刻攔截越界索引。而是在 DisAppearCells 和 AppearCells 時,根據 索引值判斷是否越界,抽出兩個方法:
//是否有效索引(只將顯示索引顯示到列表中,默認為 0~cellCount 之間)
//loop時,認為任意索引都是有效的,以使非 0~cellCount 的區域能夠顯示元素,之后再在 ConvertIndexToValid 轉換
protected virtual bool IsValidIndex(int index)
{if (m_Loop) { return true; }else { return index >= 0 && index < m_CellCount; }
}
//轉換索引至有效(默認無需處理)
//loop時,將任意索引數轉到 [0~cellCount-1] 中
protected virtual int ConvertIndexToValid(int index)
{if (m_Loop) { return (index % m_CellCount + m_CellCount) % m_CellCount; }else { return index; }
}
處理完本條之后,理論上,將 Content的寬度設為 無限大,就可以直接支持首尾無限循環。
不過,最好還是選擇位置重置的方案。
2、滑動時,從初始位置開始,只要向左/向右滑出超過1個重置單位,就將 Content 重置回起始位置。
(注意,滑動過程,完全無需考慮Cell顯示問題,完全由①處理,可將Content想象為一張整圖)
(注意這里說的 1個重置單位 = 1核心內容寬度 + 1個spacingX)
3、將 Content 寬度擴為原核心內容寬度擴展的N倍,使其滿足位置重置的基本條件。
????????支持從初始位置開始,向左向右各滑動1個重置單位,需要在兩側至少各擴展出1個重置單位。
????????但為了避免 翻超1個重置單位觸及邊緣 觸發回彈,可以額外多出1或2個重置單位。
? ? ? ? 注意,擴寬 Content 更多倍是毫無成本的!這里只是思考至少應該擴展幾倍。
????????所以,直接定為 2+2=4倍。
4、再回頭來思考 核心內容寬度 < viewport寬度 的倍數
????????在上面的基礎上,只需簡單處理:將 核心內容寬度 先翻倍,使超過 viewport寬度。
????????注意,這種情況下,在Viewport中會出現多個同一Cell,屬于正常現象。
5、重置Content位置。但小心不能影響到 ScrollRect 內部的 速度值計算!!!
? ? ? ? 我用了不少時間才解決這個問題。
? ? ? ? 位置的重置,不能放在?OnScrollValueChanged。原因是:
? ? ? ? ①、ScrollRect 的 LateUpdate中,有邏輯為:
????????開啟慣性速度選項,進行拖拽時,會根據Content相鄰兩幀的 位置確定后續的 慣性起始速度。
? ? ? ? 因此,如果在滑動過程中,如果只突然修改 Content 位置,將會導致 速度劇變。
? ? ? ? ②、在?OnScrollValueChanged 修改?Content 位置過晚,將使 其他注冊?OnScrollValueChanged 的地方無法獲取的真實 value。
????????因此,可在?SetContentAnchoredPosition?方法中,增加一個虛方法,供子類重寫修改Content 的位置。
? ? ? ? 在具體修改Content位置時,還需注意:
? ? ? ? ①、要同時更新 m_PrevPosition,以使本幀 LateUpdate中 計算的 m_Velocity 不會因位置劇變而劇變。
? ? ? ? ②、要同時更新 m_ContentStartPosition,以使 OnDrag 中,Content位置跟隨鼠標移動時,不反復觸發此“位置超過一頁的重置邏輯”,否則下一幀 m_PrevPosition 又將執行一次偏移(上一行代碼),還是會導致速度劇變。
//Content初始位置
float contentStartPosX = -m_CellStartOffsetOnMovementAxis;
//獲取當前位置
float curContentPosX = m_Content.anchoredPosition.x;
//Content向左時,Content重置點坐標(初始位置左側1個重置寬度)
float leftResetPosX = contentStartPosX - m_LoopResetSizeOnMovementAxis;
//Content向右時,Content重置點坐標(初始位置右側1個重置寬度)
float rightResetPosX = contentStartPosX + m_LoopResetSizeOnMovementAxis;
if (curContentPosX < leftResetPosX)
{m_Content.anchoredPosition += Vector2.right * m_LoopResetSizeOnMovementAxis;//更新,以使本幀 LateUpdate中 計算的 m_Velocity 不會因位置劇變而劇變m_PrevPosition += Vector2.right * m_LoopResetSizeOnMovementAxis;//更新,以使 OnDrag 中,Content位置跟隨鼠標移動時,不反復觸發此“位置超過一頁的重置邏輯”,否則下一幀m_PrevPosition又將執行一次偏移(上一行代碼),還是會導致速度劇變m_ContentStartPosition += Vector2.right * m_LoopResetSizeOnMovementAxis;
}
else if (curContentPosX > rightResetPosX)
{m_Content.anchoredPosition += Vector2.left * m_LoopResetSizeOnMovementAxis;//更新,以使本幀 LateUpdate中 計算的 m_Velocity 不會因位置劇變而劇變m_PrevPosition += Vector2.left * m_LoopResetSizeOnMovementAxis;//更新,以使 OnDrag 中,Content位置跟隨鼠標移動時,不反復觸發此“位置超過一頁的重置邏輯”,否則下一幀m_PrevPosition又將執行一次偏移(上一行代碼),還是會導致速度劇變m_ContentStartPosition += Vector2.left * m_LoopResetSizeOnMovementAxis;
}
----------------------------------------- NRatel割 -----------------------------------------
實現效果:
源碼:
https://github.com/NRatel/Unity-ListViewhttps://github.com/NRatel/Unity-ListView