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); } } }