|
@@ -0,0 +1,479 @@
|
|
|
|
+using UnityEngine;
|
|
|
|
+using System.Collections;
|
|
|
|
+using UnityEngine.UI;
|
|
|
|
+using System.Collections.Generic;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/// <summary>
|
|
|
|
+/// 设置Item内容的委托
|
|
|
|
+/// </summary>
|
|
|
|
+/// <param name="item">Item对象</param>
|
|
|
|
+/// <param name="wrapIndex">Item在Grid中的序号</param>
|
|
|
|
+/// <param name="realIndex">当前Item在List中的序号</param>
|
|
|
|
+public delegate void OnUpdateItem(GameObject item, int wrapIndex, int realIndex);
|
|
|
|
+
|
|
|
|
+/// <summary>
|
|
|
|
+/// 已显示了所有item的委托
|
|
|
|
+/// </summary>
|
|
|
|
+public delegate void OnEatUpItem();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+///注意:当横排滚动时,不能使用横向拉伸布局,不然会有一些额外的多余尺寸问题
|
|
|
|
+/// 同理,当竖排滚动时,不能使用纵向拉伸布局
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/// <summary>
|
|
|
|
+/// 排列方式枚举
|
|
|
|
+/// </summary>
|
|
|
|
+public enum ArrangeType
|
|
|
|
+{
|
|
|
|
+ Horizontal = 0,//水平排列
|
|
|
|
+ Vertical = 1,//垂直排列
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+public class UIScrollScript : MonoBehaviour
|
|
|
|
+{
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Item的宽
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int cell_x = 100;
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Item的高
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int cell_y = 100;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Item最小索引
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int minIndex = 0;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Item最大数量
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int maxCount = 0;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 排列方式
|
|
|
|
+ /// </summary>
|
|
|
|
+ public ArrangeType arrangeType = ArrangeType.Horizontal;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 行列的个数
|
|
|
|
+ /// </summary>
|
|
|
|
+ public int ConstraintCount = 0;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 当前RectTransform对象
|
|
|
|
+ /// </summary>
|
|
|
|
+ RectTransform mRTrans;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// ScrollRect组件
|
|
|
|
+ /// </summary>
|
|
|
|
+ public ScrollRect mScroll;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Pivot为中心点的坐标
|
|
|
|
+ /// </summary>
|
|
|
|
+ Vector2 pivotChildFactor = new Vector2(0.5f, 0.5f);
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 滚动方向
|
|
|
|
+ /// </summary>
|
|
|
|
+ bool mHorizontal;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 子物体集合
|
|
|
|
+ /// </summary>
|
|
|
|
+ List<Transform> ChildList = new List<Transform>();
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 初始化时区域的长度或者高度的一半
|
|
|
|
+ /// </summary>
|
|
|
|
+ private float extents = 0;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// SrollRect的尺寸
|
|
|
|
+ /// </summary>
|
|
|
|
+ Vector2 SR_size = Vector2.zero;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// SrollRect的尺寸
|
|
|
|
+ /// </summary>
|
|
|
|
+ Vector3[] conners = new Vector3[4];
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// ScrollRect的初始位置
|
|
|
|
+ /// </summary>
|
|
|
|
+ Vector2 startPos;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 设置Item的委托
|
|
|
|
+ /// </summary>
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 一行中元素的间距
|
|
|
|
+ /// </summary>
|
|
|
|
+ [SerializeField] private float XSpacing = 0;
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 一列中元素的间距
|
|
|
|
+ /// </summary>
|
|
|
|
+ [SerializeField] private float YSpacing = 0;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 初始化赋值(目前只会执行一次)
|
|
|
|
+ /// </summary>
|
|
|
|
+ public void Init()
|
|
|
|
+ {
|
|
|
|
+ mRTrans = transform.GetComponent<RectTransform>();
|
|
|
|
+ mHorizontal = mScroll.horizontal;
|
|
|
|
+
|
|
|
|
+ SR_size = mScroll.transform.GetComponent<RectTransform>().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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// ScrollRect复位(比如:重新进入该页面时)
|
|
|
|
+ /// </summary>
|
|
|
|
+ public void ResetPosition()
|
|
|
|
+ {
|
|
|
|
+ this.transform.localPosition = startPos;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ [ContextMenu("ResetToInit")]
|
|
|
|
+ public void ResetToInit()
|
|
|
|
+ {
|
|
|
|
+ ResetPosition();
|
|
|
|
+ Init();
|
|
|
|
+ InitList(minIndex, maxCount);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 初始化mChild链表
|
|
|
|
+ /// </summary>
|
|
|
|
+ ///
|
|
|
|
+ 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();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 更新最大索引数
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="minIndex"></param>
|
|
|
|
+ /// <param name="maxIndex"></param>
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 更新Content的尺寸
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="pos"></param>
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// ScrollRect的回调
|
|
|
|
+ /// </summary>
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 获取子物体的位置
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="temp"></param>
|
|
|
|
+ /// <param name="isRefreshRect"></param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 计算realIndex
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="pos"></param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 通知更新Item的数据
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="item"></param>
|
|
|
|
+ /// <param name="index"></param>
|
|
|
|
+ /// <param name="realIndex"></param>
|
|
|
|
+ void UpdateItem(Transform item, int index, int realIndex)
|
|
|
|
+ {
|
|
|
|
+ if (onUpdateItem != null)
|
|
|
|
+ {
|
|
|
|
+ onUpdateItem(item.gameObject, index, realIndex);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|