using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Collections.Generic;
///
/// 设置Item内容的委托
///
/// Item对象
/// Item在Grid中的序号
/// 当前Item在List中的序号
public delegate void OnUpdateItem(GameObject item, int wrapIndex, int realIndex);
///
/// 已显示了所有item的委托
///
public delegate void OnEatUpItem();
///注意:当横排滚动时,不能使用横向拉伸布局,不然会有一些额外的多余尺寸问题
/// 同理,当竖排滚动时,不能使用纵向拉伸布局
///
/// 排列方式枚举
///
public enum ArrangeType
{
Horizontal = 0,//水平排列
Vertical = 1,//垂直排列
}
public class UIScrollScript : MonoBehaviour
{
///
/// Item的宽
///
public int cell_x = 100;
///
/// Item的高
///
public int cell_y = 100;
///
/// Item最小索引
///
public int minIndex = 0;
///
/// Item最大数量
///
public int maxCount = 0;
///
/// 排列方式
///
public ArrangeType arrangeType = ArrangeType.Horizontal;
///
/// 行列的个数
///
public int ConstraintCount = 0;
///
/// 当前RectTransform对象
///
RectTransform mRTrans;
///
/// ScrollRect组件
///
public ScrollRect mScroll;
///
/// Pivot为中心点的坐标
///
Vector2 pivotChildFactor = new Vector2(0.5f, 0.5f);
///
/// 滚动方向
///
bool mHorizontal;
///
/// 子物体集合
///
List ChildList = new List();
///
/// 初始化时区域的长度或者高度的一半
///
private float extents = 0;
///
/// SrollRect的尺寸
///
Vector2 SR_size = Vector2.zero;
///
/// SrollRect的尺寸
///
Vector3[] conners = new Vector3[4];
///
/// ScrollRect的初始位置
///
Vector2 startPos;
///
/// 设置Item的委托
///
public OnUpdateItem onUpdateItem;
public OnEatUpItem onEatUpItem;
private bool isHandEatup = false;
[SerializeField] private float leftOffset = 0;
[SerializeField] private float rightOffset = 0;
[SerializeField] private float topOffset = 0;
[SerializeField] private float bottomOffset = 0;
///
/// 一行中元素的间距
///
[SerializeField] private float XSpacing = 0;
///
/// 一列中元素的间距
///
[SerializeField] private float YSpacing = 0;
///
/// 初始化赋值(目前只会执行一次)
///
public void Init()
{
mRTrans = transform.GetComponent();
mHorizontal = mScroll.horizontal;
SR_size = mScroll.transform.GetComponent().rect.size;
//四角坐标 横着数
conners[0] = new Vector3(-SR_size.x / 2f, SR_size.y / 2f, 0);
conners[1] = new Vector3(SR_size.x / 2f, SR_size.y / 2f, 0);
conners[2] = new Vector3(-SR_size.x / 2f, -SR_size.y / 2f, 0);
conners[3] = new Vector3(SR_size.x / 2f, -SR_size.y / 2f, 0);
//将四角坐标转化为绝对的坐标值
for (int i = 0; i < 4; i++)
{
Vector3 temp = mScroll.transform.TransformPoint(conners[i]);
conners[i].x = temp.x;
conners[i].y = temp.y;
}
mRTrans.pivot = new Vector2(0, 1);//设置mRTrans的中心在左上角
mScroll.onValueChanged.AddListener(delegate { WrapContent(); });//添加滚动事件回调
startPos = this.transform.localPosition;
}
///
/// ScrollRect复位(比如:重新进入该页面时)
///
public void ResetPosition()
{
this.transform.localPosition = startPos;
}
[ContextMenu("ResetToInit")]
public void ResetToInit()
{
ResetPosition();
Init();
InitList(minIndex, maxCount);
}
///
/// 初始化mChild链表
///
///
public void InitList(int minIndex, int maxIndex)
{
isHandEatup = false;
this.minIndex = minIndex;
this.maxCount = maxIndex;
//如果横排数量为0,则设置默认为1,避免手误
if (ConstraintCount <= 0)
{
ConstraintCount = 1;
}
//如果最小索引大于了最大索引,则让他俩相等,避免手误
if (this.minIndex > this.maxCount)
{
this.minIndex = this.maxCount;
}
//Init();
ChildList.Clear();
for (int i = 0; i < transform.childCount; i++)
{
ChildList.Add(this.transform.GetChild(i));
}
for (int j = 0; j < ChildList.Count; j++)
{
Transform temp = ChildList[j];
RectTransform trf = temp as RectTransform;
trf.anchorMax = new Vector2(0, 1);
trf.anchorMin = new Vector2(0, 1);
trf.pivot = pivotChildFactor;
trf.anchoredPosition3D = Vector3.zero;
}
ResetChildPosition();
}
///
/// 更新最大索引数
///
///
///
public void UpdateCount(int minIndex, int maxIndex)
{
this.minIndex = minIndex;
this.maxCount = maxIndex;
isHandEatup = false;
}
void ResetChildPosition()
{
///行数 列数
int rows = 1, cols = 1;
//初始位置
Vector2 startAxis = new Vector2((cell_x / 2f) + leftOffset, (-cell_y / 2f) - topOffset);
if (arrangeType == ArrangeType.Horizontal)
{
rows = ConstraintCount;
cols = (int)Mathf.Ceil((float)ChildList.Count / rows);
//(所有子物体宽度的总和 + 所有间距的总和 + 左偏移量 + 右偏移量)/2
extents = ((cols * cell_x) + (XSpacing * (cols - 1) + leftOffset + rightOffset)) * 0.5f;
}
else if (arrangeType == ArrangeType.Vertical)
{
cols = ConstraintCount;
rows = (int)Mathf.Ceil((float)ChildList.Count / (float)cols);
//(所有子物体高度的总和 + 所有间距的总和 + 上偏移量 + 下偏移量)/2
extents = ((rows * cell_y) + (YSpacing * (rows - 1) + topOffset + bottomOffset)) * 0.5f;
}
///初始为所有子物体排列布局
for (int i = 0; i < ChildList.Count; i++)
{
Transform temp = ChildList[i];
RectTransform trf = temp as RectTransform;
trf.anchorMax = new Vector2(0, 1);
trf.anchorMin = new Vector2(0, 1);
trf.pivot = pivotChildFactor;
trf.anchoredPosition3D = Vector3.zero;
int x = 0, y = 0;//行列号
Vector2 pos = Vector2.zero;
if (arrangeType == ArrangeType.Vertical)
{
x = i / cols; //(行号:0 -1 - 2 - 3 - 4...)
y = i % cols;//(列号:0 - 1 - 0 - 1...)
pos = new Vector2((startAxis.x + y * cell_x) + y * XSpacing, (startAxis.y - x * cell_y) - (x * YSpacing));
}
else if (arrangeType == ArrangeType.Horizontal)
{
x = i % rows;//行号:0 - 1 - 0 - 1...
y = i / rows;//列号:0 -1 - 2 - 3 - 4...
pos = new Vector2((startAxis.x + y * cell_x) + y * XSpacing, (startAxis.y - x * cell_y) - (x * YSpacing));
}
temp.localPosition = pos;
if (minIndex == maxCount || (i >= minIndex && i < maxCount))
{
temp.gameObject.SetActive(true);
UpdateRectsize(pos);//更新panel的尺寸
UpdateItem(temp, i, i);
temp.name = i.ToString();
}
else
{
temp.gameObject.SetActive(false);
}
}
}
///
/// 更新Content的尺寸
///
///
void UpdateRectsize(Vector2 pos)
{
if (arrangeType == ArrangeType.Horizontal)
{
mRTrans.sizeDelta = new Vector2(pos.x + cell_x * 0.5f + rightOffset, ConstraintCount * cell_y + (ConstraintCount - 1) * YSpacing + topOffset + bottomOffset);
}
else
{
mRTrans.sizeDelta = new Vector2(ConstraintCount * cell_x + (ConstraintCount - 1) * XSpacing + leftOffset + rightOffset
, -pos.y + cell_y * 0.5f + bottomOffset);
}
}
///
/// ScrollRect的回调
///
public void WrapContent(bool isRefresh = false)
{
for (int i = 0; i < ChildList.Count; i++)
{
Transform temp = ChildList[i];
bool isRefreshRect = false;
Vector2 pos = GetChildPos(temp, out isRefreshRect);
int realIndex = GetRealIndex(pos);
if (minIndex == maxCount || (realIndex >= minIndex && realIndex < maxCount))
{
temp.gameObject.SetActive(true);
if (isRefreshRect)
{
//Debug.Log("是否需要刷新");
UpdateRectsize(pos);
}
temp.localPosition = pos;
if (temp.name != realIndex.ToString())
{
//设置Item内容
UpdateItem(temp, i, realIndex);
temp.name = realIndex.ToString();
}
else if (temp.name == realIndex.ToString() && isRefresh)
{
//设置Item内容
UpdateItem(temp, i, realIndex);
temp.name = realIndex.ToString();
}
}
else if (realIndex >= maxCount)
{
if (!isHandEatup)
{
Debug.Log("WrapContent===>数据已用完");
//通知所有要显示的数据已用完
if (onEatUpItem != null)
{
onEatUpItem();
}
isHandEatup = true;
}
return;
}
}
}
///
/// 获取子物体的位置
///
///
///
///
Vector2 GetChildPos(Transform temp, out bool isRefreshRect)
{
isRefreshRect = false;
Vector3[] conner_local = new Vector3[4];
for (int i = 0; i < 4; i++)
{
conner_local[i] = this.transform.InverseTransformPoint(conners[i]);
}
//计算ScrollRect的中心坐标 相对于this的坐标
Vector2 center = (conner_local[3] + conner_local[0]) / 2f;
Vector2 pos = Vector2.zero;
if (mHorizontal)
{
float distance = temp.localPosition.x - center.x;
pos = temp.localPosition;
if (distance < -extents)
{
isRefreshRect = true;
pos.x += extents * 2f - leftOffset - rightOffset + XSpacing;
}
else if (distance > extents)
{
pos.x -= extents * 2f - leftOffset - rightOffset + XSpacing;
}
}
else
{
if (temp != null)
{
float distance = temp.localPosition.y - center.y;
pos = temp.localPosition;
if (distance < -extents)
{
pos.y += extents * 2f - bottomOffset - topOffset + YSpacing;
}
else if (distance > extents)
{
isRefreshRect = true;
pos.y -= extents * 2f - bottomOffset - topOffset + YSpacing;
}
}
}
return pos;
}
///
/// 计算realIndex
///
///
///
private int GetRealIndex(Vector2 pos)
{
int x = (int)Mathf.Ceil((-pos.y - topOffset + YSpacing) / (cell_y + YSpacing)) - 1;
int y = (int)Mathf.Ceil((pos.x - leftOffset + XSpacing) / (cell_x + XSpacing)) - 1;
//int x = (int)Mathf.Ceil(-pos.y / cell_y) - 1;//行号
//int y = (int)Mathf.Ceil(pos.x / cell_x) - 1;//列号
int realIndex;
if (arrangeType == ArrangeType.Vertical)
{
realIndex = x * ConstraintCount + y;
}
else
{
realIndex = x + ConstraintCount * y;
}
return realIndex;
}
///
/// 通知更新Item的数据
///
///
///
///
void UpdateItem(Transform item, int index, int realIndex)
{
if (onUpdateItem != null)
{
onUpdateItem(item.gameObject, index, realIndex);
}
}
}