/********************************************************************
文件: ScrollViewGridEx.cs
作者: 梦语
邮箱: 1982614048@qq.com
创建时间: 2024/4/5 17:11
最后修改: 梦语
最后修改时间: 2024/04/23 09:21:55
功能:
*********************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Sirenix.OdinInspector;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Ether
{
public class ScrollViewGridEx : SerializedMonoBehaviour
{
public enum ScrollDirection
{
[InspectorName("从上到下")]
TopToDown,
[InspectorName("从下到上")]
DownToTop,
[InspectorName("从左到右")]
LeftToRight,
[InspectorName("从右到左")]
RightToLeft,
}
///
/// 格子数据
///
public class CellInfo
{
public int index;
public Vector3 pos; //已左上角为点
public GameObject item;
}
#region 参数
private ScrollRect scrollRect;
public ScrollRect ScrollRect => scrollRect;
[ShowInInspector]
private RectTransform content;
[Tooltip("该属性优先"), LabelText("格子预制体")]
[ShowIf("@this.cellPrefabPath == null || this.cellPrefabPath == \"\"")]
public GameObject cellPrefab;
[Tooltip("格子预制体属性优先"), LabelText("格子预制体路径")]
[ShowIf("@this.cellPrefab == null")]
public string cellPrefabPath;
private ScrollDirection scrollDirection;
[SerializeField, ExposeProperty, Label("方向")]
public ScrollDirection Direction
{
get => scrollDirection;
set
{
scrollDirection = value;
ScrollRect scroll = GetComponent();
//Debug.LogError(scrollDirection);
switch (value)
{
case ScrollDirection.TopToDown:
case ScrollDirection.DownToTop:
scroll.horizontal = false;
scroll.vertical = true;
break;
case ScrollDirection.LeftToRight:
case ScrollDirection.RightToLeft:
scroll.horizontal = true;
scroll.vertical = false;
break;
default:
break;
}
}
}
[SerializeField, Label("一行或一列数量")]
private int rowOrColNum = 1;
[SerializeField, Label("间隔")]
private Vector2 specing;
#endregion
private void Awake()
{
scrollRect = GetComponent();
if (scrollRect == null)
{
Debug.LogError("该组件需要对应的ScrollRect! 请检查!!!");
return;
}
if (content == null)
{
content = scrollRect?.content;
}
if (content == null)
{
Transform viewport = scrollRect?.viewport;
content = viewport != null ? viewport?.GetChild(0).GetComponent() : null;
}
if (content == null)
{
Debug.LogError("获取不到对应的ScrollView中的content! 请检查!!!");
return;
}
content.anchorMin = new Vector2(0, 1);
content.anchorMax = new Vector2(0, 1);
if (cellPrefab == null)
{
cellPrefab = LoaderTools.LoadAsset(cellPrefabPath);
}
if (cellPrefab == null)
{
Debug.LogError("获取不到对应的格子预制体! 请检查!!!");
return;
}
cellPrefab.SetActive(false);
scrollRect.onValueChanged.RemoveAllListeners();
scrollRect.onValueChanged.AddListener(ScrollOnDragListener);
}
Action actionEvent;
List cellInfoList = new List();
public void Init(Action dataAction, int dataMaxNum = -1)
{
cellInfoList.Clear();
actionEvent = dataAction;
if (dataMaxNum != -1)
{
RefreshData(dataMaxNum);
}
}
public void RefreshData(int dataMaxNum)
{
if (cellPrefab == null)
{
Debug.LogError("获取不到对应的格子预制体! 请检查!!!");
return;
}
foreach (CellInfo info in cellInfoList)
{
if (info.item != null)
{
info.item.PushPool();
}
info.PushPool();
}
for (int i = 0; i < dataMaxNum; i++)
{
CellInfo cellInfo = PoolManager.Inst.Get();
cellInfo.index = i;
cellInfoList.Add(cellInfo);
}
Vector2 cellSize = cellPrefab.GetSize();
switch (scrollDirection)
{
case ScrollDirection.TopToDown:
case ScrollDirection.DownToTop:
int rowNum = dataMaxNum / rowOrColNum + 1;
float width = (cellSize.x + specing.x) * rowOrColNum - specing.x;
float height = (cellSize.y + specing.y) * rowNum - specing.y;
content.gameObject.SetSize(new Vector2(width, height));
for (int i = 0; i < cellInfoList.Count; i++)
{
CellInfo cellInfo = cellInfoList[i];
int curCol = i % rowOrColNum;
int curRow = i / rowOrColNum;
float posX = curCol * (cellSize.x + specing.x);
float posY = -curRow * (cellSize.y + specing.y);
cellInfo.pos = new Vector3(posX, posY);
//Debug.Log(cellInfo.pos);
}
break;
case ScrollDirection.LeftToRight:
case ScrollDirection.RightToLeft:
int colNum = dataMaxNum / rowOrColNum + 1;
float contentWidth = (cellSize.x + specing.x) * colNum - specing.x;
float contentHeight = (cellSize.y + specing.y) * rowOrColNum - specing.y;
content.gameObject.SetSize(new Vector2(contentWidth, contentHeight));
for (int i = 0; i < cellInfoList.Count; i++)
{
CellInfo cellInfo = cellInfoList[i];
int curRow = i % rowOrColNum;
int curCol = i / rowOrColNum;
float posX = curCol * (cellSize.x + specing.x);
float posY = -curRow * (cellSize.y + specing.y);
cellInfo.pos = new Vector3(posX, posY);
//Debug.Log(cellInfo.pos);
}
break;
default:
break;
}
CheckCellItemData();
}
Vector2 lastPos = Vector2.zero;
Vector2 curPos = Vector2.zero;
private void ScrollOnDragListener(Vector2 pos)
{
curPos = pos;
if (curPos != lastPos)
{
//Debug.Log(pos);
CheckCellItemData();
}
lastPos = curPos;
}
private void CheckCellItemData()
{
for (int i = 0; i < cellInfoList.Count; i++)
{
CellInfo cellInfo = cellInfoList[i];
//Debug.Log(cellInfo.pos);
if (IsInScrollRange(cellInfo))
{
if (cellInfo.item == null)
{
cellInfo.item = PoolManager.Inst.Get(cellPrefab, content);
cellInfo.item.transform.localPosition = cellInfo.pos;
cellInfo.item.name = cellInfo.index.ToString();
}
actionEvent?.Invoke(cellInfo.item, cellInfo.index);
}
else
{
if (cellInfo.item != null)
{
cellInfo.item.PushPool(cellPrefab.name);
cellInfo.item = null;
}
}
}
}
private bool IsInScrollRange(CellInfo cellInfo)
{
Vector2 contentPos = content.anchoredPosition;
Vector2 scrollSize = scrollRect.gameObject.GetSize();
Vector2 cellSize = cellPrefab.GetSize();
float topPos = -contentPos.y + cellSize.y;
float downPos = -contentPos.y - scrollSize.y;
float leftPos = -contentPos.x - cellSize.x;
float rightPos = -contentPos.x + scrollSize.x;
//Debug.Log($"cellInfo.pos:{cellInfo.pos} topPos:{topPos}/downPos:{downPos}/leftPos:{leftPos}/rightPos:{rightPos}");
if (cellInfo.pos.x >= leftPos && cellInfo.pos.x <= rightPos && cellInfo.pos.y >= downPos && cellInfo.pos.y <= topPos)
{
return true;
}
return false;
}
#if UNITY_EDITOR
[MenuItem("GameObject/UIEx/ScrollViewEx/ScrollViewGridEx", priority = 1)]
private static void CreateScrollViewEx(MenuCommand menuCmd)
{
GameObject selection = Selection.activeGameObject;
GameObject gameObject;
if (selection != null)
{
Debug.Log($"选择物体:{selection.name}");
// 获取物体的根 Canvas
Canvas rootCanvas = selection.GetComponentInParent