feat: Generates group views dynamically
This commit is contained in:
160
Assets/Cryville.Common/Unity/UI/RecyclerView.cs
Normal file
160
Assets/Cryville.Common/Unity/UI/RecyclerView.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Cryville.Common.Unity.UI {
|
||||
public delegate void LoadItemHandler(int index, GameObject gameObject);
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
public class RecyclerView : UIBehaviour {
|
||||
[SerializeField]
|
||||
private GameObject m_itemTemplate;
|
||||
public GameObject ItemTemplate {
|
||||
get { return m_itemTemplate; }
|
||||
set { m_itemTemplate = value; /*OnTemplateUpdate();*/ }
|
||||
}
|
||||
public LoadItemHandler LoadItem { private get; set; }
|
||||
|
||||
public enum Axis {
|
||||
Horizontal = 0,
|
||||
Vertical = 1,
|
||||
}
|
||||
[SerializeField]
|
||||
private Axis m_direction;
|
||||
public Axis Direction {
|
||||
get { return m_direction; }
|
||||
set { m_direction = value; /*OnFrameUpdate();*/ }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
RectOffset m_padding;
|
||||
public RectOffset Padding {
|
||||
get => m_padding;
|
||||
set => m_padding = value;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
float m_spacing;
|
||||
public float Spacing {
|
||||
get => m_spacing;
|
||||
set => m_spacing = value;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private int m_itemCount = 3;
|
||||
public int ItemCount {
|
||||
get { return m_itemCount; }
|
||||
set { m_itemCount = value; /*OnRefresh();*/ }
|
||||
}
|
||||
|
||||
RectTransform _rectTransform;
|
||||
protected override void Awake() {
|
||||
_rectTransform = GetComponent<RectTransform>();
|
||||
}
|
||||
|
||||
const float _placeholderLength = 100;
|
||||
int _firstIndex, _lastIndex;
|
||||
readonly Stack<GameObject> _pool = new();
|
||||
void Update() {
|
||||
int axis = (int)m_direction;
|
||||
int sign = m_direction == 0 ? 1 : -1;
|
||||
float padding = axis == 0 ? m_padding.left : m_padding.top;
|
||||
if (_rectTransform.parent is not RectTransform parentTransform)
|
||||
throw new InvalidOperationException("Parent transform is not RectTransform");
|
||||
|
||||
Vector2 apos = _rectTransform.anchoredPosition;
|
||||
float pos = apos[axis] + padding;
|
||||
float childPos = _firstIndex * _placeholderLength + padding;
|
||||
float visibleLength = parentTransform.rect.size[axis];
|
||||
|
||||
//// Add preceding
|
||||
//while (_firstIndex > 0 && childPos > pos) {
|
||||
// var child = Rent();
|
||||
// child.transform.SetAsFirstSibling();
|
||||
// LoadItem(--_firstIndex, child);
|
||||
// pos += GetLength(axis, child.transform) - _placeholderLength;
|
||||
// childPos -= _placeholderLength;
|
||||
//}
|
||||
|
||||
//// Remove preceding
|
||||
//while (_firstIndex < _lastIndex) {
|
||||
// var child = transform.GetChild(0);
|
||||
// float len = GetLength(axis, child.transform);
|
||||
// if (childPos + len > pos) break;
|
||||
// Return(child.gameObject);
|
||||
// _firstIndex++;
|
||||
// pos += _placeholderLength - len;
|
||||
// childPos += _placeholderLength;
|
||||
//}
|
||||
|
||||
//apos[axis] = pos;
|
||||
//_rectTransform.anchoredPosition = apos;
|
||||
|
||||
// Layout existing
|
||||
int index = _firstIndex;
|
||||
float layoutToPos = pos + visibleLength;
|
||||
for (; index < _lastIndex && childPos < layoutToPos; index++) {
|
||||
var child = (RectTransform)transform.GetChild(index - _firstIndex);
|
||||
LayoutChild(axis, sign, ref childPos, child);
|
||||
}
|
||||
|
||||
// Remove following
|
||||
for (; _lastIndex > index; --_lastIndex) {
|
||||
var child = (RectTransform)transform.GetChild(index - _firstIndex);
|
||||
Return(child.gameObject);
|
||||
}
|
||||
|
||||
// Add following (and layout)
|
||||
while (_lastIndex < m_itemCount && childPos < layoutToPos) {
|
||||
var child = Rent();
|
||||
if (child.transform is not RectTransform childTransform)
|
||||
throw new InvalidOperationException("Child transform is not RectTransform");
|
||||
childTransform.SetSiblingIndex(_lastIndex - _firstIndex);
|
||||
LoadItem(_lastIndex++, child);
|
||||
LayoutChild(axis, sign, ref childPos, childTransform);
|
||||
}
|
||||
|
||||
Vector2 gsize = _rectTransform.sizeDelta;
|
||||
gsize[axis] = childPos + (m_itemCount - _lastIndex) * _placeholderLength + (axis == 0 ? m_padding.horizontal : m_padding.vertical);
|
||||
_rectTransform.sizeDelta = gsize;
|
||||
}
|
||||
static float GetLength(int axis, Transform child) {
|
||||
if (child is not RectTransform childTransform)
|
||||
throw new InvalidOperationException("Child transform is not RectTransform");
|
||||
return LayoutUtility.GetPreferredSize(childTransform, axis);
|
||||
}
|
||||
void LayoutChild(int axis, int sign, ref float childPos, RectTransform childTransform) {
|
||||
Vector2 cpos = childTransform.anchoredPosition;
|
||||
cpos[axis] = childPos * sign;
|
||||
cpos[axis ^ 1] = axis == 1 ? m_padding.left : m_padding.top;
|
||||
childTransform.anchoredPosition = cpos;
|
||||
|
||||
Vector2 size = childTransform.sizeDelta;
|
||||
size[axis ^ 1] = _rectTransform.rect.size[axis ^ 1] - (axis == 1 ? m_padding.horizontal : m_padding.vertical);
|
||||
childTransform.sizeDelta = size;
|
||||
|
||||
childPos += GetLength(axis, childTransform) + m_spacing;
|
||||
}
|
||||
|
||||
GameObject Rent() {
|
||||
if (_pool.TryPop(out var ret)) {
|
||||
ret.SetActive(true);
|
||||
return ret;
|
||||
}
|
||||
return Instantiate(m_itemTemplate, transform, false);
|
||||
}
|
||||
void Return(GameObject child) {
|
||||
child.transform.SetAsLastSibling();
|
||||
child.SetActive(false);
|
||||
_pool.Push(child);
|
||||
}
|
||||
|
||||
public void InvalidateAll() {
|
||||
for (; _lastIndex > _firstIndex; --_lastIndex) {
|
||||
var child = (RectTransform)transform.GetChild(0);
|
||||
Return(child.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/Unity/UI/RecyclerView.cs.meta
Normal file
11
Assets/Cryville.Common/Unity/UI/RecyclerView.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1257c1f4490f1d64bb8fc52a9abed1ae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user