mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-16 00:07:52 +08:00
Some UI cleanups, improving caching and reduce image allocation
This commit is contained in:
@ -12,33 +12,57 @@ namespace UnityExplorer.UI.Widgets
|
||||
public CellViewHolder(GameObject uiRoot)
|
||||
{
|
||||
this.UIRoot = uiRoot;
|
||||
this.m_rect = uiRoot.GetComponent<RectTransform>();
|
||||
this.Rect = uiRoot.GetComponent<RectTransform>();
|
||||
m_enabled = uiRoot.activeSelf;
|
||||
}
|
||||
|
||||
public bool Enabled => m_enabled;
|
||||
private bool m_enabled;
|
||||
|
||||
public GameObject UIRoot;
|
||||
public RectTransform Rect => m_rect;
|
||||
private RectTransform m_rect;
|
||||
public GameObject UIRoot { get; }
|
||||
public RectTransform Rect { get; }
|
||||
|
||||
public void Disable()
|
||||
private GameObject m_content;
|
||||
|
||||
public GameObject SetContent(GameObject newContent)
|
||||
{
|
||||
m_enabled = false;
|
||||
UIRoot.SetActive(false);
|
||||
var ret = m_content;
|
||||
|
||||
if (ret && newContent && ret.ReferenceEqual(newContent))
|
||||
return null;
|
||||
|
||||
newContent.transform.SetParent(this.UIRoot.transform, false);
|
||||
(this as ICell).Enable();
|
||||
|
||||
m_content = newContent;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
public GameObject DisableContent()
|
||||
{
|
||||
var ret = m_content;
|
||||
(this as ICell).Disable();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ICell.Enable()
|
||||
{
|
||||
m_enabled = true;
|
||||
UIRoot.SetActive(true);
|
||||
}
|
||||
|
||||
void ICell.Disable()
|
||||
{
|
||||
m_enabled = false;
|
||||
UIRoot.SetActive(false);
|
||||
}
|
||||
|
||||
public static RectTransform CreatePrototypeCell(GameObject parent)
|
||||
{
|
||||
var prototype = UIFactory.CreateVerticalGroup(parent, "PrototypeCell", true, true, true, true, 0, new Vector4(1, 0, 0, 0),
|
||||
new Color(0.15f, 0.15f, 0.15f), TextAnchor.MiddleCenter);
|
||||
// using an image on the cell view holder is fine, we only need to make about 20-50 of these per pool.
|
||||
var prototype = UIFactory.CreateVerticalGroup(parent, "PrototypeCell", true, true, true, true, 0, new Vector4(0, 0, 0, 0),
|
||||
new Color(0.11f, 0.11f, 0.11f), TextAnchor.MiddleCenter);
|
||||
|
||||
var rect = prototype.GetComponent<RectTransform>();
|
||||
rect.anchorMin = new Vector2(0, 1);
|
||||
rect.anchorMax = new Vector2(0, 1);
|
||||
@ -47,6 +71,10 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
prototype.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
var sepObj = UIFactory.CreateUIObject("separator", prototype);
|
||||
sepObj.AddComponent<Image>().color = Color.black;
|
||||
UIFactory.SetLayoutElement(sepObj, minHeight: 1, preferredHeight: 1, flexibleHeight: 0);
|
||||
|
||||
prototype.SetActive(false);
|
||||
|
||||
return rect;
|
||||
|
@ -18,22 +18,14 @@ namespace UnityExplorer.UI.Widgets
|
||||
public class DataHeightCache
|
||||
{
|
||||
private ScrollPool ScrollPool { get; }
|
||||
//private DataHeightCache SisterCache { get; }
|
||||
|
||||
public DataHeightCache(ScrollPool scrollPool)
|
||||
{
|
||||
ScrollPool = scrollPool;
|
||||
}
|
||||
|
||||
public DataHeightCache(ScrollPool scrollPool, DataHeightCache sisterCache) : this(scrollPool)
|
||||
{
|
||||
//this.SisterCache = sisterCache;
|
||||
|
||||
//for (int i = 0; i < scrollPool.DataSource.ItemCount; i++)
|
||||
// Add(sisterCache[ScrollPool.DataSource.GetRealIndexOfTempIndex(i)]);
|
||||
}
|
||||
|
||||
private readonly List<DataViewInfo> heightCache = new List<DataViewInfo>();
|
||||
// initialize with a reasonably sized pool, most caches will allocate a fair bit.
|
||||
private readonly List<DataViewInfo> heightCache = new List<DataViewInfo>(16384);
|
||||
|
||||
public DataViewInfo this[int index]
|
||||
{
|
||||
@ -118,14 +110,23 @@ namespace UnityExplorer.UI.Widgets
|
||||
/// <summary>Get the data index at the specific position of the total height cache.</summary>
|
||||
public int GetDataIndexAtPosition(float desiredHeight, out DataViewInfo cache)
|
||||
{
|
||||
cache = null;
|
||||
cache = default;
|
||||
int rangeIndex = GetRangeIndexOfPosition(desiredHeight);
|
||||
|
||||
if (rangeIndex < 0)
|
||||
throw new Exception("Range index (" + rangeIndex + ") is below 0");
|
||||
{
|
||||
ExplorerCore.LogWarning("RangeIndex < 0? " + rangeIndex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rangeCache.Count <= rangeIndex)
|
||||
throw new Exception("Range index (" + rangeIndex + ") exceeded rangeCache count (" + rangeCache.Count + ")");
|
||||
{
|
||||
ExplorerCore.LogWarning("Want range index " + rangeIndex + " but count is " + rangeCache.Count);
|
||||
RebuildCache();
|
||||
rangeIndex = GetRangeIndexOfPosition(desiredHeight);
|
||||
if (rangeCache.Count <= rangeIndex)
|
||||
throw new Exception("Range index (" + rangeIndex + ") exceeded rangeCache count (" + rangeCache.Count + ")");
|
||||
}
|
||||
|
||||
int dataIndex = rangeCache[rangeIndex];
|
||||
cache = heightCache[dataIndex];
|
||||
@ -134,16 +135,13 @@ namespace UnityExplorer.UI.Widgets
|
||||
}
|
||||
|
||||
/// <summary>Set a given data index with the specified value.</summary>
|
||||
public void SetIndex(int dataIndex, float height, bool ignoreDataCount = false)
|
||||
public void SetIndex(int dataIndex, float height)
|
||||
{
|
||||
if (!ignoreDataCount)
|
||||
if (dataIndex >= ScrollPool.DataSource.ItemCount)
|
||||
{
|
||||
if (dataIndex >= ScrollPool.DataSource.ItemCount)
|
||||
{
|
||||
while (heightCache.Count > dataIndex)
|
||||
RemoveLast();
|
||||
return;
|
||||
}
|
||||
while (heightCache.Count > dataIndex)
|
||||
RemoveLast();
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataIndex >= heightCache.Count)
|
||||
@ -175,14 +173,10 @@ namespace UnityExplorer.UI.Widgets
|
||||
int rangeIndex = GetRangeCeilingOfPosition(cache.startPosition);
|
||||
int spread = GetRangeSpread(cache.startPosition, height);
|
||||
|
||||
// check if our range cache is "corrupt" or not. If so we need to do a quick rebuild,
|
||||
// so that each cell's start position is correct again.
|
||||
if (rangeCache.Count <= rangeIndex || rangeCache[rangeIndex] != dataIndex)
|
||||
if (rangeCache.Count <= rangeIndex)
|
||||
{
|
||||
RebuildStartPositions(ignoreDataCount);
|
||||
// get these values again after rebuilding
|
||||
rangeIndex = GetRangeCeilingOfPosition(cache.startPosition);
|
||||
spread = GetRangeSpread(cache.startPosition, height);
|
||||
RebuildCache();
|
||||
return;
|
||||
}
|
||||
|
||||
if (spread != cache.normalizedSpread)
|
||||
@ -196,7 +190,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
// In some rare cases we may not find our data index at the expected range index.
|
||||
// We can make some educated guesses and find the real index pretty quickly.
|
||||
int minStart = rangeCache[dataIndex];
|
||||
int minStart = GetRangeIndexOfPosition(dataIndex * DefaultHeight);
|
||||
for (int i = minStart; i < rangeCache.Count; i++)
|
||||
{
|
||||
if (rangeCache[i] == dataIndex)
|
||||
@ -211,16 +205,16 @@ namespace UnityExplorer.UI.Widgets
|
||||
// This should never happen. We might be in a rebuild right now so don't
|
||||
// rebuild again, we could overflow the stack. Just log it.
|
||||
ExplorerCore.LogWarning($"DataHeightCache: Looking for range index of data {dataIndex} but reached the end and didn't find it.");
|
||||
ExplorerCore.Log($"startPos: {cache.startPosition}, rangeIndex: {rangeIndex}, total height: {TotalHeight}");
|
||||
return;
|
||||
}
|
||||
|
||||
// our data index is further down. add the min difference and try again.
|
||||
// the iterator will add 1 on the next loop so account for that.
|
||||
// also, add the (spread - 1) of the cell we found at this index to skip it.
|
||||
var spreadCurr = heightCache[rangeCache[i]].normalizedSpread;
|
||||
int jmp = dataIndex - rangeCache[i] - 1;
|
||||
jmp += heightCache[rangeCache[i]].normalizedSpread - 1;
|
||||
i += jmp < 1 ? 0 : jmp;
|
||||
jmp += spreadCurr - 2;
|
||||
i = (jmp < 1 ? i : i + jmp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,19 +226,19 @@ namespace UnityExplorer.UI.Widgets
|
||||
if (rangeCache[rangeIndex] == dataIndex)
|
||||
rangeCache.Insert(rangeIndex, dataIndex);
|
||||
else
|
||||
ExplorerCore.LogWarning($"DataHeightCache error increasing spread of data {dataIndex}, " +
|
||||
$"the value at range {rangeIndex} is {rangeCache[rangeIndex]}!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// need to remove
|
||||
for (int i = 0; i < -spreadDiff; i++)
|
||||
{
|
||||
if (rangeCache[rangeIndex] == dataIndex)
|
||||
rangeCache.RemoveAt(rangeIndex);
|
||||
else
|
||||
ExplorerCore.LogWarning($"DataHeightCache error decreasing spread of data {dataIndex}, " +
|
||||
$"the value at range {rangeIndex} is {rangeCache[rangeIndex]}!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,11 +251,11 @@ namespace UnityExplorer.UI.Widgets
|
||||
//}
|
||||
}
|
||||
|
||||
private void RebuildStartPositions(bool ignoreDataCount)
|
||||
private void RebuildCache()
|
||||
{
|
||||
//start at 1 because 0's start pos is always 0
|
||||
for (int i = 1; i < heightCache.Count; i++)
|
||||
SetIndex(i, heightCache[i].height, ignoreDataCount);
|
||||
SetIndex(i, heightCache[i].height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,11 @@ using UnityExplorer.UI.Models;
|
||||
|
||||
namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
public struct CellInfo
|
||||
{
|
||||
public int cellIndex, dataIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An object-pooled ScrollRect, attempts to support content of any size and provide a scrollbar for it.
|
||||
/// </summary>
|
||||
@ -62,7 +67,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
private readonly List<ICell> CellPool = new List<ICell>();
|
||||
|
||||
internal DataHeightCache HeightCache;
|
||||
internal DataHeightCache tempHeightCache;
|
||||
|
||||
private float TotalDataHeight => HeightCache.TotalHeight + contentLayout.padding.top + contentLayout.padding.bottom;
|
||||
|
||||
@ -114,7 +118,10 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
public void Rebuild()
|
||||
{
|
||||
RecreateCellPool(true, true, null);
|
||||
SetRecycleViewBounds(false);
|
||||
SetScrollBounds();
|
||||
|
||||
RecreateCellPool(true, true);
|
||||
writingLocked = false;
|
||||
Content.anchoredPosition = Vector2.zero;
|
||||
UpdateSliderHandle(true);
|
||||
@ -128,22 +135,23 @@ namespace UnityExplorer.UI.Widgets
|
||||
UpdateSliderHandle(true);
|
||||
}
|
||||
|
||||
public void EnableTempCache()
|
||||
public void RecreateHeightCache()
|
||||
{
|
||||
if (tempHeightCache == null)
|
||||
tempHeightCache = HeightCache;
|
||||
//if (tempHeightCache == null)
|
||||
// tempHeightCache = HeightCache;
|
||||
|
||||
HeightCache = new DataHeightCache(this, tempHeightCache);
|
||||
HeightCache = new DataHeightCache(this);
|
||||
CheckDataSourceCountChange(out _);
|
||||
}
|
||||
|
||||
public void DisableTempCache()
|
||||
{
|
||||
if (tempHeightCache == null)
|
||||
return;
|
||||
//public void DisableTempCache()
|
||||
//{
|
||||
// if (tempHeightCache == null)
|
||||
// return;
|
||||
|
||||
HeightCache = tempHeightCache;
|
||||
tempHeightCache = null;
|
||||
}
|
||||
// HeightCache = tempHeightCache;
|
||||
// tempHeightCache = null;
|
||||
//}
|
||||
|
||||
public void RefreshCells(bool reloadData)
|
||||
{
|
||||
@ -252,23 +260,15 @@ namespace UnityExplorer.UI.Widgets
|
||||
RecycleViewBounds = new Vector2(Viewport.MinY() + HalfThreshold, Viewport.MaxY() - HalfThreshold);
|
||||
|
||||
if (checkHeightGrow && prevViewportHeight < Viewport.rect.height && prevViewportHeight != 0.0f)
|
||||
ret = RecreateCellPool(false, false, null);
|
||||
ret = RecreateCellPool(false, false);
|
||||
|
||||
prevViewportHeight = Viewport.rect.height;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private bool RecreateCellPool(bool forceRecreate, bool resetDataIndex, bool? setTempCacheEnabledTo)
|
||||
private bool RecreateCellPool(bool forceRecreate, bool resetDataIndex)
|
||||
{
|
||||
if (setTempCacheEnabledTo != null)
|
||||
{
|
||||
if (setTempCacheEnabledTo == true)
|
||||
EnableTempCache();
|
||||
else if (setTempCacheEnabledTo == false)
|
||||
DisableTempCache();
|
||||
}
|
||||
|
||||
CheckDataSourceCountChange(out _);
|
||||
|
||||
var requiredCoverage = Math.Abs(RecycleViewBounds.y - RecycleViewBounds.x);
|
||||
@ -310,7 +310,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
// Refresh methods
|
||||
|
||||
private struct CellInfo { public int cellIndex, dataIndex; }
|
||||
private CellInfo _cellInfo = new CellInfo();
|
||||
|
||||
private IEnumerator<CellInfo> GetPoolEnumerator()
|
||||
{
|
||||
@ -319,11 +319,9 @@ namespace UnityExplorer.UI.Widgets
|
||||
int iterated = 0;
|
||||
while (iterated < CellPool.Count)
|
||||
{
|
||||
yield return new CellInfo()
|
||||
{
|
||||
cellIndex = cellIdx,
|
||||
dataIndex = dataIndex
|
||||
};
|
||||
_cellInfo.cellIndex = cellIdx;
|
||||
_cellInfo.dataIndex = dataIndex;
|
||||
yield return _cellInfo;
|
||||
|
||||
cellIdx++;
|
||||
if (cellIdx >= CellPool.Count)
|
||||
@ -407,7 +405,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
cachedCell.Enable();
|
||||
DataSource.SetCell(cachedCell, dataIndex);
|
||||
|
||||
//LayoutRebuilder.ForceRebuildLayoutImmediate(cachedCell.Rect);
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(cachedCell.Rect);
|
||||
HeightCache.SetIndex(dataIndex, cachedCell.Rect.rect.height);
|
||||
}
|
||||
|
||||
@ -443,11 +441,11 @@ namespace UnityExplorer.UI.Widgets
|
||||
ScrollRect.m_ContentStartPosition += vector;
|
||||
ScrollRect.m_PrevPosition += vector;
|
||||
|
||||
// LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
|
||||
prevAnchoredPos = ScrollRect.content.anchoredPosition;
|
||||
|
||||
SetScrollBounds();
|
||||
|
||||
//WritingLocked = true;
|
||||
UpdateSliderHandle();
|
||||
}
|
||||
|
||||
@ -487,6 +485,8 @@ namespace UnityExplorer.UI.Widgets
|
||||
topPoolIndex = (topPoolIndex + 1) % CellPool.Count;
|
||||
}
|
||||
|
||||
//LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
|
||||
|
||||
return -recycledheight;
|
||||
}
|
||||
|
||||
@ -528,6 +528,8 @@ namespace UnityExplorer.UI.Widgets
|
||||
bottomPoolIndex = (bottomPoolIndex - 1 + CellPool.Count) % CellPool.Count;
|
||||
}
|
||||
|
||||
//LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
|
||||
|
||||
return recycledheight;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user