123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- 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, maxCount * cell_y + (maxCount - 1) * YSpacing + topOffset + bottomOffset);
- }
- else
- {
- mRTrans.sizeDelta = new Vector2(maxCount * cell_x + (maxCount - 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);
- }
- }
- }
|