mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-16 00:07:52 +08:00
Rewriting everything from scratch, developed generic ObjectPool system
This commit is contained in:
@ -1,83 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
//using UnityEngine;
|
||||
//using UnityEngine.UI;
|
||||
|
||||
namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
public class CellViewHolder : ICell
|
||||
{
|
||||
public CellViewHolder(GameObject uiRoot)
|
||||
{
|
||||
this.UIRoot = uiRoot;
|
||||
this.Rect = uiRoot.GetComponent<RectTransform>();
|
||||
m_enabled = uiRoot.activeSelf;
|
||||
}
|
||||
//namespace UnityExplorer.UI.Widgets
|
||||
//{
|
||||
// public class CellViewHolder : ICell
|
||||
// {
|
||||
// public CellViewHolder(GameObject uiRoot)
|
||||
// {
|
||||
// this.UIRoot = uiRoot;
|
||||
// this.Rect = uiRoot.GetComponent<RectTransform>();
|
||||
// m_enabled = uiRoot.activeSelf;
|
||||
// }
|
||||
|
||||
public bool Enabled => m_enabled;
|
||||
private bool m_enabled;
|
||||
// public bool Enabled => m_enabled;
|
||||
// private bool m_enabled;
|
||||
|
||||
public GameObject UIRoot { get; }
|
||||
public RectTransform Rect { get; }
|
||||
// public GameObject UIRoot { get; }
|
||||
// public RectTransform Rect { get; }
|
||||
|
||||
private GameObject m_content;
|
||||
// private GameObject m_content;
|
||||
|
||||
public GameObject SetContent(GameObject newContent)
|
||||
{
|
||||
var ret = m_content;
|
||||
// public GameObject SetContent(GameObject newContent)
|
||||
// {
|
||||
// var ret = m_content;
|
||||
|
||||
if (ret && newContent && ret.ReferenceEqual(newContent))
|
||||
return null;
|
||||
// if (ret && newContent && ret.ReferenceEqual(newContent))
|
||||
// return null;
|
||||
|
||||
newContent.transform.SetParent(this.UIRoot.transform, false);
|
||||
(this as ICell).Enable();
|
||||
// newContent.transform.SetParent(this.UIRoot.transform, false);
|
||||
// (this as ICell).Enable();
|
||||
|
||||
m_content = newContent;
|
||||
return ret;
|
||||
}
|
||||
// m_content = newContent;
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
public GameObject DisableContent()
|
||||
{
|
||||
var ret = m_content;
|
||||
(this as ICell).Disable();
|
||||
return ret;
|
||||
}
|
||||
// public GameObject DisableContent()
|
||||
// {
|
||||
// var ret = m_content;
|
||||
// (this as ICell).Disable();
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
void ICell.Enable()
|
||||
{
|
||||
m_enabled = true;
|
||||
UIRoot.SetActive(true);
|
||||
}
|
||||
// void ICell.Enable()
|
||||
// {
|
||||
// m_enabled = true;
|
||||
// UIRoot.SetActive(true);
|
||||
// }
|
||||
|
||||
void ICell.Disable()
|
||||
{
|
||||
m_enabled = false;
|
||||
UIRoot.SetActive(false);
|
||||
}
|
||||
// void ICell.Disable()
|
||||
// {
|
||||
// m_enabled = false;
|
||||
// UIRoot.SetActive(false);
|
||||
// }
|
||||
|
||||
public static RectTransform CreatePrototypeCell(GameObject parent)
|
||||
{
|
||||
// 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);
|
||||
// public static RectTransform CreatePrototypeCell(GameObject parent)
|
||||
// {
|
||||
// // 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);
|
||||
rect.pivot = new Vector2(0.5f, 1);
|
||||
rect.sizeDelta = new Vector2(100, 30);
|
||||
// var rect = prototype.GetComponent<RectTransform>();
|
||||
// rect.anchorMin = new Vector2(0, 1);
|
||||
// rect.anchorMax = new Vector2(0, 1);
|
||||
// rect.pivot = new Vector2(0.5f, 1);
|
||||
// rect.sizeDelta = new Vector2(100, 30);
|
||||
|
||||
prototype.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
// 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);
|
||||
// var sepObj = UIFactory.CreateUIObject("separator", prototype);
|
||||
// sepObj.AddComponent<Image>().color = Color.black;
|
||||
// UIFactory.SetLayoutElement(sepObj, minHeight: 1, preferredHeight: 1, flexibleHeight: 0);
|
||||
|
||||
prototype.SetActive(false);
|
||||
// prototype.SetActive(false);
|
||||
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
}
|
||||
// return rect;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
@ -15,11 +15,11 @@ namespace UnityExplorer.UI.Widgets
|
||||
public static implicit operator float(DataViewInfo it) => it.height;
|
||||
}
|
||||
|
||||
public class DataHeightCache
|
||||
public class DataHeightCache<T> where T : ICell
|
||||
{
|
||||
private ScrollPool ScrollPool { get; }
|
||||
private ScrollPool<T> ScrollPool { get; }
|
||||
|
||||
public DataHeightCache(ScrollPool scrollPool)
|
||||
public DataHeightCache(ScrollPool<T> scrollPool)
|
||||
{
|
||||
ScrollPool = scrollPool;
|
||||
}
|
||||
@ -38,7 +38,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
public float TotalHeight => totalHeight;
|
||||
private float totalHeight;
|
||||
|
||||
public float DefaultHeight => m_defaultHeight ?? (float)(m_defaultHeight = ScrollPool.PrototypeCell.rect.height);
|
||||
public float DefaultHeight => m_defaultHeight ?? (float)(m_defaultHeight = ScrollPool.PrototypeHeight);
|
||||
private float? m_defaultHeight;
|
||||
|
||||
/// <summary>
|
||||
@ -100,8 +100,16 @@ namespace UnityExplorer.UI.Widgets
|
||||
return;
|
||||
|
||||
var val = heightCache[heightCache.Count - 1];
|
||||
totalHeight -= val;
|
||||
totalHeight -= val;
|
||||
heightCache.RemoveAt(heightCache.Count - 1);
|
||||
|
||||
int idx = heightCache.Count;
|
||||
if (idx > 0)
|
||||
{
|
||||
while (rangeCache[rangeCache.Count - 1] == idx)
|
||||
rangeCache.RemoveAt(rangeCache.Count - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Get the data index at the specific position of the total height cache.</summary>
|
||||
|
@ -3,10 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.UI.ObjectPool;
|
||||
|
||||
namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
public interface ICell
|
||||
public interface ICell : IPooledObject
|
||||
{
|
||||
bool Enabled { get; }
|
||||
|
||||
|
@ -6,15 +6,15 @@ using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
public interface IPoolDataSource
|
||||
public interface IPoolDataSource<T> where T : ICell
|
||||
{
|
||||
int ItemCount { get; }
|
||||
|
||||
void SetCell(ICell cell, int index);
|
||||
void DisableCell(ICell cell, int index);
|
||||
|
||||
int GetRealIndexOfTempIndex(int tempIndex);
|
||||
|
||||
ICell CreateCell(RectTransform cellTransform);
|
||||
void OnCellBorrowed(T cell);
|
||||
void OnCellReturned(T cell);
|
||||
|
||||
void SetCell(T cell, int index);
|
||||
void DisableCell(T cell, int index);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.UI.Models;
|
||||
using UnityExplorer.UI.ObjectPool;
|
||||
|
||||
namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
@ -15,20 +16,31 @@ namespace UnityExplorer.UI.Widgets
|
||||
public int cellIndex, dataIndex;
|
||||
}
|
||||
|
||||
//public abstract class ScrollPool : UIBehaviourModel
|
||||
//{
|
||||
// public abstract IPoolDataSource DataSource { get; set; }
|
||||
// public abstract RectTransform PrototypeCell { get; }
|
||||
//
|
||||
// public abstract void Initialize(IPoolDataSource dataSource);
|
||||
//
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// An object-pooled ScrollRect, attempts to support content of any size and provide a scrollbar for it.
|
||||
/// </summary>
|
||||
public class ScrollPool : UIBehaviourModel
|
||||
public class ScrollPool<T> : UIBehaviourModel where T : ICell
|
||||
{
|
||||
public ScrollPool(ScrollRect scrollRect)
|
||||
{
|
||||
this.ScrollRect = scrollRect;
|
||||
}
|
||||
|
||||
public IPoolDataSource DataSource;
|
||||
public RectTransform PrototypeCell;
|
||||
public IPoolDataSource<T> DataSource { get; set; }
|
||||
|
||||
private float PrototypeHeight => PrototypeCell.rect.height;
|
||||
public float PrototypeHeight => _protoHeight ?? (float)(_protoHeight = Pool<T>.Instance.DefaultHeight);
|
||||
private float? _protoHeight;
|
||||
|
||||
//private float PrototypeHeight => DefaultHeight.rect.height;
|
||||
|
||||
public int ExtraPoolCells => 6;
|
||||
public float RecycleThreshold => PrototypeHeight * ExtraPoolCells;
|
||||
@ -64,9 +76,9 @@ namespace UnityExplorer.UI.Widgets
|
||||
private int bottomDataIndex;
|
||||
private int TopDataIndex => Math.Max(0, bottomDataIndex - CellPool.Count + 1);
|
||||
|
||||
private readonly List<ICell> CellPool = new List<ICell>();
|
||||
private readonly List<T> CellPool = new List<T>();
|
||||
|
||||
internal DataHeightCache HeightCache;
|
||||
internal DataHeightCache<T> HeightCache;
|
||||
|
||||
private float TotalDataHeight => HeightCache.TotalHeight + contentLayout.padding.top + contentLayout.padding.bottom;
|
||||
|
||||
@ -99,8 +111,16 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
private float prevContentHeight;
|
||||
|
||||
public void SetUninitialized()
|
||||
{
|
||||
m_initialized = false;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (!m_initialized || !ScrollRect || DataSource == null)
|
||||
return;
|
||||
|
||||
if (writingLocked && timeofLastWriteLock < Time.time)
|
||||
writingLocked = false;
|
||||
|
||||
@ -125,6 +145,8 @@ namespace UnityExplorer.UI.Widgets
|
||||
writingLocked = false;
|
||||
Content.anchoredPosition = Vector2.zero;
|
||||
UpdateSliderHandle(true);
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
public void RefreshAndJumpToTop()
|
||||
@ -137,22 +159,10 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
public void RecreateHeightCache()
|
||||
{
|
||||
//if (tempHeightCache == null)
|
||||
// tempHeightCache = HeightCache;
|
||||
|
||||
HeightCache = new DataHeightCache(this);
|
||||
HeightCache = new DataHeightCache<T>(this);
|
||||
CheckDataSourceCountChange(out _);
|
||||
}
|
||||
|
||||
//public void DisableTempCache()
|
||||
//{
|
||||
// if (tempHeightCache == null)
|
||||
// return;
|
||||
|
||||
// HeightCache = tempHeightCache;
|
||||
// tempHeightCache = null;
|
||||
//}
|
||||
|
||||
public void RefreshCells(bool reloadData)
|
||||
{
|
||||
RefreshCells(reloadData, true);
|
||||
@ -160,15 +170,14 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
// Initialize
|
||||
|
||||
public void Initialize(IPoolDataSource dataSource, RectTransform prototypeCell)
|
||||
private bool m_initialized;
|
||||
|
||||
public void Initialize(IPoolDataSource<T> dataSource)
|
||||
{
|
||||
if (!prototypeCell)
|
||||
throw new Exception("No prototype cell set, cannot initialize");
|
||||
// Ensure the pool for the cell type is initialized.
|
||||
Pool<T>.GetPool();
|
||||
|
||||
this.PrototypeCell = prototypeCell;
|
||||
PrototypeCell.transform.SetParent(Viewport, false);
|
||||
|
||||
HeightCache = new DataHeightCache(this);
|
||||
HeightCache = new DataHeightCache<T>(this);
|
||||
DataSource = dataSource;
|
||||
|
||||
this.contentLayout = ScrollRect.content.GetComponent<VerticalLayoutGroup>();
|
||||
@ -200,6 +209,8 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
// add onValueChanged listener after setup
|
||||
ScrollRect.onValueChanged.AddListener(OnValueChangedListener);
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
private void SetScrollBounds()
|
||||
@ -209,14 +220,22 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
// Cell pool
|
||||
|
||||
private void CreateCellPool(bool andResetDataIndex = true)
|
||||
public void ReturnCells()
|
||||
{
|
||||
if (CellPool.Any())
|
||||
{
|
||||
foreach (var cell in CellPool)
|
||||
GameObject.Destroy(cell.Rect.gameObject);
|
||||
{
|
||||
DataSource.OnCellReturned(cell);
|
||||
Pool<T>.Return(cell);
|
||||
}
|
||||
CellPool.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCellPool(bool andResetDataIndex = true)
|
||||
{
|
||||
ReturnCells();
|
||||
|
||||
float currentPoolCoverage = 0f;
|
||||
float requiredCoverage = ScrollRect.viewport.rect.height + RecycleThreshold;
|
||||
@ -230,11 +249,17 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
bottomPoolIndex++;
|
||||
|
||||
//Instantiate and add to Pool
|
||||
RectTransform rect = GameObject.Instantiate(PrototypeCell.gameObject).GetComponent<RectTransform>();
|
||||
rect.gameObject.SetActive(true);
|
||||
rect.name = $"Cell_{CellPool.Count}";
|
||||
var cell = DataSource.CreateCell(rect);
|
||||
////Instantiate and add to Pool
|
||||
//RectTransform rect = GameObject.Instantiate(PrototypeCell.gameObject).GetComponent<RectTransform>();
|
||||
//rect.gameObject.SetActive(true);
|
||||
//rect.name = $"Cell_{CellPool.Count}";
|
||||
//var cell = DataSource.CreateCell(rect);
|
||||
//CellPool.Add(cell);
|
||||
//rect.SetParent(ScrollRect.content, false);
|
||||
|
||||
var cell = Pool<T>.Borrow();
|
||||
DataSource.OnCellBorrowed(cell);
|
||||
var rect = cell.Rect;
|
||||
CellPool.Add(cell);
|
||||
rect.SetParent(ScrollRect.content, false);
|
||||
|
||||
@ -400,7 +425,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
ScrollRect.UpdatePrevData();
|
||||
}
|
||||
|
||||
private void SetCell(ICell cachedCell, int dataIndex)
|
||||
private void SetCell(T cachedCell, int dataIndex)
|
||||
{
|
||||
cachedCell.Enable();
|
||||
DataSource.SetCell(cachedCell, dataIndex);
|
||||
|
Reference in New Issue
Block a user