初始化

This commit is contained in:
come
2025-07-26 16:56:42 +08:00
parent 8291dbb91c
commit fa81439a8c
2574 changed files with 328492 additions and 2170 deletions

View File

@@ -0,0 +1,34 @@
using Sirenix.OdinInspector;
namespace UnityEngine.UI
{
public abstract partial class LoopScrollRect
{
[SerializeField]
[LabelText("缓存父级对象")]
internal RectTransform u_CacheRect;
[SerializeField]
[LabelText("最大可点击数")]
[MinValue(1)]
internal int u_MaxClickCount = 1;
[SerializeField]
[LabelText("自动取消上一个选择")]
[MinValue(1)]
internal bool u_AutoCancelLast = true;
[SerializeField]
[LabelText("重复点击则取消")]
internal bool u_RepetitionCancel;
internal int u_StartLine => StartLine; //可见的第一行
internal int u_CurrentLines => CurrentLines; //滚动中的当前行数
internal int u_TotalLines => TotalLines; //总数
internal int u_EndLine => Mathf.Min(u_StartLine + u_CurrentLines, u_TotalLines); //可见的最后一行
internal int u_ContentConstraintCount => contentConstraintCount; //限制 行/列 数
internal float u_ContentSpacing => contentSpacing; //间隔
internal int u_ItemStart => itemTypeStart; //当前显示的第一个的Index
internal int u_ItemEnd => itemTypeEnd; //当前显示的最后一个index 被+1了注意
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1dafb5ec7aa14af1bbefeb1f5c0d25e6
timeCreated: 1684240446

View File

@@ -0,0 +1,152 @@
//------------------------------------------------------------
// Author: 亦亦
// Mail: 379338943@qq.com
// Data: 2023年2月12日
//------------------------------------------------------------
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace YIUIFramework
{
public partial class YIUILoopScroll<TData, TItemRenderer>: LoopScrollPrefabAsyncSource, LoopScrollDataSource
where TItemRenderer : UIBase
{
/// <summary>
/// 列表项渲染器
/// </summary>
/// <param name="index">数据的索引</param>
/// <param name="data">数据项</param>
/// <param name="item">显示对象</param>
/// <param name="select">是否被选中</param>
public delegate void ListItemRenderer(int index, TData data, TItemRenderer item, bool select);
private ListItemRenderer m_ItemRenderer;
private UIBindVo m_BindVo;
private IList<TData> m_Data;
private LoopScrollRect m_Owner;
private ObjAsyncCache<TItemRenderer> m_UIBasePool;
private Dictionary<Transform, TItemRenderer> m_ItemTransformDic = new Dictionary<Transform, TItemRenderer>();
private Dictionary<Transform, int> m_ItemTransformIndexDic = new Dictionary<Transform, int>();
public YIUILoopScroll(
LoopScrollRect owner,
ListItemRenderer itemRenderer)
{
var data = UIBindHelper.GetBindVoByType<TItemRenderer>();
if (data == null) return;
m_ItemTransformDic.Clear();
m_ItemTransformIndexDic.Clear();
m_BindVo = data.Value;
m_ItemRenderer = itemRenderer;
m_UIBasePool = new ObjAsyncCache<TItemRenderer>(OnCreateItemRenderer);
m_Owner = owner;
m_Owner.prefabSource = this;
m_Owner.dataSource = this;
InitCacheParent();
InitClearContent();
}
#region Private
private void InitCacheParent()
{
if (m_Owner.u_CacheRect != null)
{
m_Owner.u_CacheRect.gameObject.SetActive(false);
}
else
{
var cacheObj = new GameObject("Cache");
var cacheRect = cacheObj.GetOrAddComponent<RectTransform>();
m_Owner.u_CacheRect = cacheRect;
cacheRect.SetParent(m_Owner.transform, false);
cacheObj.SetActive(false);
}
}
//不应该初始化时有内容 所有不管是什么全部摧毁
private void InitClearContent()
{
var count = Content.childCount;
for (var i = 0; i < count; i++)
{
var child = Content.GetChild(0);
Object.DestroyImmediate(child.gameObject);
}
}
private TItemRenderer GetItemRendererByDic(Transform tsf)
{
if (m_ItemTransformDic.TryGetValue(tsf, out var value))
{
return value;
}
Debug.LogError($"{tsf.name} 没找到这个关联对象 请检查错误");
return null;
}
private void AddItemRendererByDic(Transform tsf, TItemRenderer item)
{
m_ItemTransformDic.TryAdd(tsf, item);
}
private int GetItemIndex(Transform tsf)
{
return m_ItemTransformIndexDic.GetValueOrDefault(tsf, -1);
}
private void ResetItemIndex(Transform tsf, int index)
{
m_ItemTransformIndexDic[tsf] = index;
}
#endregion
#region LoopScrollRect Interface
private async UniTask<TItemRenderer> OnCreateItemRenderer()
{
var uiBase = await YIUIFactory.InstantiateAsync<TItemRenderer>(m_BindVo);
AddItemRendererByDic(uiBase.OwnerRectTransform, uiBase);
return AddOnClickEvent(uiBase);
}
public async UniTask<GameObject> GetObject(int index)
{
var uiBase = await m_UIBasePool.Get();
return uiBase.OwnerGameObject;
}
public void ReturnObject(Transform transform)
{
var uiBase = GetItemRendererByDic(transform);
if (uiBase == null) return;
m_UIBasePool.Put(uiBase);
ResetItemIndex(transform, -1);
transform.SetParent(m_Owner.u_CacheRect, false);
}
public void ProvideData(Transform transform, int index)
{
var uiBase = GetItemRendererByDic(transform);
if (uiBase == null) return;
ResetItemIndex(transform, index);
var select = m_OnClickItemHashSet.Contains(index);
if (m_Data == null)
{
Debug.LogError($"当前没有设定数据 m_Data == null");
return;
}
m_ItemRenderer?.Invoke(index, m_Data[index], uiBase, select);
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ecbeceb50fb342d6960d03203f971d89
timeCreated: 1684288929

View File

@@ -0,0 +1,139 @@
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace YIUIFramework
{
/// <summary>
/// 对外可调用API
/// </summary>
public partial class YIUILoopScroll<TData, TItemRenderer>
{
//设置数据 然后刷新
//不管是要修改数据长度 还是数据变更了 都用此方法刷新
public async UniTask SetDataRefresh(IList<TData> data)
{
m_Data = data;
m_Owner.totalCount = data.Count;
await RefillCells();
}
//所有数据全部刷新 全部显示 不基于无限循环了
//适用于数据量很少的情况 需要动态显示的
public async UniTask SetDataRefreshShowAll(IList<TData> data)
{
m_Data = data;
m_Owner.totalCount = data.Count;
await RefillCells(0,99999);
await ScrollToCellWithinTime(0,0);
}
//刷新时默认选中某个索引数据
//注意这里相当于+=操作 如果你会频繁调用这个方法
//又想每次刷新选中不同的索引
//那么你应该先自行调用一次 ClearSelect
public async UniTask SetDataRefresh(IList<TData> data, int index)
{
SetDefaultSelect(index);
await SetDataRefresh(data);
}
//同上 请看注释 注意使用方式
public async UniTask SetDataRefresh(IList<TData> data, List<int> index)
{
SetDefaultSelect(index);
await SetDataRefresh(data);
}
//如果 < 0 则表示这个对象在对象池里
public int GetItemIndex(TItemRenderer item)
{
return GetItemIndex(item.OwnerRectTransform);
}
//只能获取当前可见的对象
public TItemRenderer GetItemByIndex(int index, bool log = true)
{
if (index < ItemStart || index >= ItemEnd) return null;
var childIndex = index - ItemStart;
if (childIndex < 0 || childIndex >= Content.childCount)
{
if (log)
{
Debug.LogError($"索引错误 请检查 index:{index} Start:{ItemStart} childIndex:{childIndex} childCount:{Content.childCount}");
}
return null;
}
var transform = Content.GetChild(childIndex);
var uiBase = GetItemRendererByDic(transform);
return uiBase;
}
//判断某个对象是否被选中
public bool IsSelect(TItemRenderer item)
{
return m_OnClickItemHashSet.Contains(GetItemIndex(item));
}
//就获取目前显示的这几个数据
public List<TData> GetShowData()
{
var listData = new List<TData>();
for (var i = ItemStart; i < ItemEnd; i++)
{
listData.Add(m_Data[i]);
}
return listData;
}
#region ..
//获取当前所有被选择的索引
public List<int> GetSelectIndex()
{
return m_OnClickItemQueue.ToList();
}
//只能得到当前可见的 不可见的拿不到
public List<TItemRenderer> GetSelectItem()
{
var selectList = new List<TItemRenderer>();
foreach (var index in GetSelectIndex())
{
var item = GetItemByIndex(index);
if (item != null)
{
selectList.Add(item);
}
}
return selectList;
}
//获取所有被选择的数据
public List<TData> GetSelectData()
{
var selectList = new List<TData>();
foreach (var index in GetSelectIndex())
{
selectList.Add(m_Data[index]);
}
return selectList;
}
//移除某个选中的目标 然后刷新
public async UniTask RemoveSelectIndexRefresh(int index)
{
RemoveSelectIndex(index);
await RefreshCells();
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d03accccee69468dbc99efeb392c1211
timeCreated: 1684310524

View File

@@ -0,0 +1,77 @@
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace YIUIFramework
{
/// <summary>
/// 快捷方法/属性
/// </summary>
public partial class YIUILoopScroll<TData, TItemRenderer>
{
public int TotalCount => m_Owner.totalCount; //总数
public RectTransform Content => m_Owner.content;
public RectTransform CacheRect => m_Owner.u_CacheRect;
public int StartLine => m_Owner.u_StartLine; //可见的第一行
public int CurrentLines => m_Owner.u_CurrentLines; //滚动中的当前行数
public int TotalLines => m_Owner.u_TotalLines; //总数
public int EndLine => Mathf.Min(StartLine + CurrentLines, TotalLines); //可见的最后一行
public int ContentConstraintCount => m_Owner.u_ContentConstraintCount; //限制 行/列 数
public float ContentSpacing => m_Owner.u_ContentSpacing; //间隔
public int ItemStart => m_Owner.u_ItemStart; //当前显示的第一个的Index
public int ItemEnd => m_Owner.u_ItemEnd; //当前显示的最后一个index 被+1了注意
//在开始时用startItem填充单元格同时清除现有的单元格
public async UniTask RefillCells(int startItem = 0, float contentOffset = 0)
{
await m_Owner.RefillCells(startItem, contentOffset);
}
//在结束时重新填充endItem中的单元格同时清除现有的单元格
public async UniTask RefillCellsFromEnd(int endItem = 0, bool alignStart = false)
{
await m_Owner.RefillCellsFromEnd(endItem, alignStart);
}
public async UniTask RefreshCells()
{
await m_Owner.RefreshCells();
}
public void ClearCells()
{
m_Owner.ClearCells();
}
public int GetFirstItem(out float offset)
{
return m_Owner.GetFirstItem(out offset);
}
public int GetLastItem(out float offset)
{
return m_Owner.GetLastItem(out offset);
}
private int GetValidIndex(int index)
{
return Mathf.Clamp(index, 0, TotalCount - 1);
}
public async UniTask ScrollToCell(int index, float speed)
{
if (TotalCount <= 0) return;
await m_Owner.ScrollToCell(GetValidIndex(index), speed);
}
public async UniTask ScrollToCellWithinTime(int index, float time)
{
if (TotalCount <= 0) return;
await m_Owner.ScrollToCellWithinTime(GetValidIndex(index), time);
}
public void StopMovement()
{
m_Owner.StopMovement();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6ade2fde49f0414a9c45e2875d361f45
timeCreated: 1684303900

View File

@@ -0,0 +1,229 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using YIUIBind;
namespace YIUIFramework
{
/// <summary>
/// 额外点击相关
/// </summary>
public partial class YIUILoopScroll<TData, TItemRenderer>
{
/// <summary>
/// 列表元素被点击的事件
/// </summary>
public delegate void OnClickItemEvent(int index, TData data, TItemRenderer item, bool select);
private bool m_OnClickInit; //是否已初始化
private string m_ItemClickEventName; //ui中的点击UIEventP0
private OnClickItemEvent m_OnClickItemEvent; //点击回调
private Queue<int> m_OnClickItemQueue = new Queue<int>(); //当前所有已选择 遵循先进先出 有序
private HashSet<int> m_OnClickItemHashSet = new HashSet<int>(); //当前所有已选择 无序 为了更快查找
private int m_MaxClickCount = 1; //可选最大数量 >=2 就是复选 最小1
private bool m_RepetitionCancel = true; //重复选择 则取消选择
private bool m_AutoCancelLast = true; //当选择操作最大数量过后 自动取消第一个选择的 否则选择无效
public YIUILoopScroll<TData, TItemRenderer> SetOnClickInfo(
string itemClickEventName,
OnClickItemEvent onClickItemEvent)
{
if (m_OnClickInit)
{
Debug.LogError($"OnClick 相关只能初始化一次 且不能修改");
return this;
}
if (string.IsNullOrEmpty(itemClickEventName))
{
Debug.LogError($"必须有事件名称");
return this;
}
if (onClickItemEvent == null)
{
Debug.LogError($"必须有点击事件");
return this;
}
m_MaxClickCount = Mathf.Max(1, m_Owner.u_MaxClickCount);
m_ItemClickEventName = itemClickEventName;
m_OnClickItemEvent = onClickItemEvent;
m_RepetitionCancel = m_Owner.u_RepetitionCancel;
m_OnClickInit = true;
m_AutoCancelLast = m_Owner.u_AutoCancelLast;
m_OnClickItemQueue.Clear();
m_OnClickItemHashSet.Clear();
return this;
}
//reset=吧之前选择的都取消掉 讲道理应该都是true
//false出问题自己查
public void ClearSelect(bool reset = true)
{
if (reset)
{
var selectCount = m_OnClickItemHashSet.Count;
for (var i = 0; i < selectCount; i++)
{
OnClickItemQueuePeek();
}
}
m_OnClickItemQueue.Clear();
m_OnClickItemHashSet.Clear();
}
//动态改变 自动取消上一个选择的
public void ChangeAutoCancelLast(bool autoCancelLast)
{
m_AutoCancelLast = autoCancelLast;
}
//动态改变 重复选择 则取消选择
public void ChangeRepetitionCancel(bool repetitionCancel)
{
m_RepetitionCancel = repetitionCancel;
}
//动态改变 最大可选数量
public void ChangeMaxClickCount(int count, bool reset = true)
{
ClearSelect(reset);
m_MaxClickCount = Mathf.Max(1, count);
}
//传入对象 选中目标
public void OnClickItem(TItemRenderer item)
{
var index = GetItemIndex(item);
if (index < 0)
{
Debug.LogError($"无法选中一个不在显示中的对象");
return;
}
var select = OnClickItemQueueEnqueue(index);
OnClickItem(index, item, select);
}
//传入索引 选中目标
public void OnClickItem(int index)
{
if (index < 0 || index >= m_Data.Count)
{
Debug.LogError($"索引越界{index} 0 - {m_Data.Count}");
return;
}
var item = GetItemByIndex(index, false);
var select = OnClickItemQueueEnqueue(index);
if (item != null)
{
OnClickItem(index, item, select);
}
}
private bool OnClickItemQueueEnqueue(int index)
{
if (m_OnClickItemHashSet.Contains(index))
{
if (m_RepetitionCancel)
{
RemoveSelectIndex(index);
return false;
}
else
{
return true;
}
}
if (m_OnClickItemQueue.Count >= m_MaxClickCount)
{
if (m_AutoCancelLast)
{
OnClickItemQueuePeek();
}
else
{
return false;
}
}
OnClickItemHashSetAdd(index);
m_OnClickItemQueue.Enqueue(index);
return true;
}
private void SetDefaultSelect(int index)
{
OnClickItemQueueEnqueue(index);
}
private void SetDefaultSelect(List<int> indexs)
{
foreach (var index in indexs)
{
SetDefaultSelect(index);
}
}
private void OnClickItem(int index, TItemRenderer item, bool select)
{
m_OnClickItemEvent?.Invoke(index, m_Data[index], item, select);
}
private TItemRenderer AddOnClickEvent(TItemRenderer uiBase)
{
if (!m_OnClickInit) return uiBase;
var eventTable = uiBase.m_EventTable;
if (eventTable == null)
{
Debug.LogError($"目标item 没有 event表 请检查");
return uiBase;
}
var uEventClickItem = eventTable.FindEvent<UIEventP0>(m_ItemClickEventName);
if (uEventClickItem == null)
{
Debug.LogError($"当前监听的事件未找到 请检查 {typeof (TItemRenderer).Name} 中是否有这个事件 {m_ItemClickEventName}");
}
else
{
uEventClickItem.Add(() => { OnClickItem(uiBase); });
}
return uiBase;
}
private void OnClickItemQueuePeek()
{
var index = m_OnClickItemQueue.Dequeue();
OnClickItemHashSetRemove(index);
if (index < ItemStart || index >= ItemEnd) return;
var item = GetItemByIndex(index);
if (item != null)
OnClickItem(index, item, false);
}
private void OnClickItemHashSetAdd(int index)
{
m_OnClickItemHashSet.Add(index);
}
private void OnClickItemHashSetRemove(int index)
{
m_OnClickItemHashSet.Remove(index);
}
private void RemoveSelectIndex(int index)
{
var list = m_OnClickItemQueue.ToList();
list.Remove(index);
m_OnClickItemQueue = new Queue<int>(list);
OnClickItemHashSetRemove(index);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8dc0b35a94db4faaba5a440147536be3
timeCreated: 1684309606