123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.UI;
- namespace Assets.UI
- {
- /// <summary>
- /// Introduction: 无限列表
- /// Content上禁止挂载ContentSizeFilter和LayOutGroup之类组件
- /// Author: Cheng
- /// Time:
- /// </summary>
- [DisallowMultipleComponent]
- [RequireComponent(typeof (ScrollRect))]
- public class UIWrapScrollList : MonoBehaviour
- {
- public delegate void OnItemRender(int index, Transform child);
- public OnItemRender onItemRender;
- /// <summary>
- /// 排序方式
- /// </summary>
- public enum Arrangement
- {
- /// <summary>
- /// 横排
- /// </summary>
- Horizontal = 0,
- /// <summary>
- /// 竖排
- /// </summary>
- Vertical,
- }
- /// <summary>
- /// 水平对齐
- /// </summary>
- public enum HorizontalAlign
- {
- /// <summary>
- /// 居左
- /// </summary>
- Left,
- /// <summary>
- /// 居中
- /// </summary>
- Middle,
- /// <summary>
- /// 局右
- /// </summary>
- Right,
- }
- /// <summary>
- /// 垂直对齐
- /// </summary>
- public enum VerticalAlign
- {
- /// <summary>
- /// 居上
- /// </summary>
- Top,
- /// <summary>
- /// 居中
- /// </summary>
- Middle,
- /// <summary>
- /// 局下
- /// </summary>
- Bottom,
- }
- public Arrangement arrangement = Arrangement.Vertical;
- /// <summary>
- /// 当选择水平或垂直流动是有用,指每行/列最大个数
- /// </summary>
- public int MaxPerLine
- {
- get { return maxPerLine; }
- set { SetMaxPerLine(value); }
- }
- /// <summary>
- /// 行距
- /// </summary>
- public float rowSpace = 0;
- /// <summary>
- /// 列距
- /// </summary>
- public float columuSpace = 0;
- public HorizontalAlign horizontalAlign = HorizontalAlign.Left;
- public VerticalAlign verticalAlign = VerticalAlign.Top;
- /// <summary>
- /// 边缘留空 上
- /// </summary>
- public float marginTop = 0;
- /// <summary>
- /// 边缘留空 下
- /// </summary>
- public float marginBottom = 0;
- /// <summary>
- /// 边缘留空 左
- /// </summary>
- public float marginLeft = 0;
- /// <summary>
- /// 边缘留空 右
- /// </summary>
- public float marginRight = 0;
- /// <summary>
- /// 渲染子节点
- /// </summary>
- public GameObject Child
- {
- get { return item; }
- set { SetItem(value); }
- }
- /// <summary>
- /// 总个数
- /// </summary>
- public int ChildCount
- {
- get { return childCount; }
- set { SetChildCount(value, true); }
- }
- /// <summary>
- /// 设置显示窗口大小
- /// </summary>
- public Vector2 ViewPort
- {
- get { return viewPort; }
- set { SetViewPort(value); }
- }
- public GameObject item;
- ScrollRect scrollRect;
- Vector2 viewPort;
- RectTransform content;
- Vector2 itemSize;
- List<Transform> items;
- Dictionary<int, int> contains;
- List<int> outOfContains;
- int childCount; //需要渲染的总数据个数
- int scrollLineIndex; //当前第一个元素索引
- int totalCount; //在UI中显示的个数(不乘以maxPerLine)
- Vector2 startPos; //第一个元素所在位置
- int startIndex; //当前渲染起始坐标
- int endIndex; //当前渲染结束坐标
- int maxPerLine;
- void Start()
- {
- maxPerLine = maxPerLine == 0 ? 1 : maxPerLine;
- items = new List<Transform>();
- contains = new Dictionary<int, int>();
- outOfContains = new List<int>();
- scrollRect = transform.GetComponent<ScrollRect>();
- content = scrollRect.content;
- if (content == null)
- {
- Debug.Log("ScrollRect " + scrollRect.gameObject.name + " Has No Content, Please Check And Retry.");
- return;
- }
- viewPort = scrollRect.viewport.rect.size;
- content.anchorMax = new Vector2(0, 1);
- content.anchorMin = new Vector2(0, 1);
- content.pivot = new Vector2(0, 1);
- ReBuild();
- }
- /// <summary>
- /// 当子节点、Mask、maxPerLine
- /// </summary>
- public void ReBuild()
- {
- if (scrollRect == null || content == null || item == null) return;
- ResetChildren();
- Vector2 maskSize = viewPort;
- int count = 0;
- if (arrangement == Arrangement.Horizontal)
- {
- count = Mathf.CeilToInt(maskSize.x/itemSize.x) + 1; //横向列数
- startPos = Vector2.zero;
- startPos.x = marginLeft;
- if (verticalAlign == VerticalAlign.Top)
- {
- startPos.y = -marginTop;
- }
- else if (verticalAlign == VerticalAlign.Middle)
- {
- startPos.y = -(maskSize.y*0.5f - (itemSize.y*maxPerLine + (maxPerLine - 1)*rowSpace)*0.5f);
- }
- else if (verticalAlign == VerticalAlign.Bottom)
- {
- startPos.y = -(maskSize.y - marginBottom - itemSize.y*maxPerLine - rowSpace*(maxPerLine - 1));
- }
- //优化:不在一开始生产所有的可见格子
- //for (int i = 0; i < count; i++)
- //{
- // for (int j = 0; j < maxPerLine; j++)
- // {
- // RectTransform child = CreateItem(i*maxPerLine + j);
- // child.localPosition = startPos +
- // new Vector2(i*itemSize.x + i*columuSpace, -j*itemSize.y - j*rowSpace);
- // }
- //}
- }
- else if (arrangement == Arrangement.Vertical)
- {
- count = Mathf.CeilToInt(maskSize.y/itemSize.y) + 1; //竖向行数
- startPos = Vector2.zero;
- startPos.y = -marginTop; //重置开始节点位置
- if (horizontalAlign == HorizontalAlign.Left)
- {
- startPos.x = marginLeft;
- }
- else if (horizontalAlign == HorizontalAlign.Middle)
- {
- startPos.x = (maskSize.x*0.5f - (itemSize.x*maxPerLine + (maxPerLine - 1)*columuSpace)*0.5f);
- }
- else if (horizontalAlign == HorizontalAlign.Right)
- {
- startPos.x = maskSize.x - marginRight - itemSize.x*maxPerLine - columuSpace*(maxPerLine - 1);
- }
- //for (int i = 0; i < count; i++)
- //{
- // for (int j = 0; j < maxPerLine; j++)
- // {
- // RectTransform child = CreateItem(i*maxPerLine + j);
- // child.localPosition = startPos +
- // new Vector2(j*itemSize.x + j*columuSpace, -i*itemSize.y - i*rowSpace);
- // }
- //}
- }
- totalCount = count;
- Debug.Log(count + " child count is " + childCount );
- SetChildCount(childCount, true);
- BackTop();
- scrollRect.onValueChanged.RemoveAllListeners();
- scrollRect.onValueChanged.AddListener(OnValueChanged);
- }
- /// <summary>
- /// 列表滚动
- /// </summary>
- /// <param name="vec"></param>
- private void OnValueChanged(Vector2 vec)
- {
- switch (arrangement)
- {
- case Arrangement.Horizontal:
- // if (vec.x < 0.0f || vec.x >= 1.0f)
- // return;
- vec.x = Mathf.Clamp(vec.x, 0, 1);
- break;
- case Arrangement.Vertical:
- // if (vec.y <= 0.0f || vec.y >= 1.0f)
- // return;
- vec.y = Mathf.Clamp(vec.y, 0, 1);
- break;
- }
- int curLineIndex = GetCurLineIndex();
- if (curLineIndex != scrollLineIndex)
- UpdateRectItem(curLineIndex, false);
- }
- /// <summary>
- /// 获取页面第一行索引
- /// </summary>
- /// <returns></returns>
- private int GetCurLineIndex()
- {
- switch (arrangement)
- {
- case Arrangement.Horizontal:
- return
- Mathf.FloorToInt(Mathf.Abs(content.anchoredPosition.x < 0.1f? content.anchoredPosition.x : 0.1f - marginLeft)/
- (columuSpace + itemSize.x));
- case Arrangement.Vertical:
- return
- Mathf.FloorToInt(Mathf.Abs(content.anchoredPosition.y>-0.1f?content.anchoredPosition.y:-0.1f - marginTop)/
- (rowSpace + itemSize.y));
- }
- return 0;
- }
- /// <summary>
- /// 更新数据(待修改问出现的才刷新)
- /// </summary>
- /// <param name="curLineIndex"></param>
- /// <param name="forceRender"></param>
- private void UpdateRectItem(int curLineIndex, bool forceRender)
- {
- if (curLineIndex < 0)
- return;
- startIndex = curLineIndex*maxPerLine;
- endIndex = (curLineIndex + totalCount)*maxPerLine;
- if (endIndex >= childCount)
- endIndex = childCount;
- contains.Clear(); //渲染序号
- outOfContains.Clear(); //items的索引
- for (int i = 0; i < items.Count; i++)//如果当前已渲染的item中包含
- {
- int index = int.Parse(items[i].gameObject.name);
- if (index < startIndex || index >= endIndex)
- {
- outOfContains.Add(i);
- items[i].gameObject.SetActive(false);
- }
- else
- {
- items[i].gameObject.SetActive(true);
- contains.Add(index, i);
- }
- }
- // *************更改渲染****************
- for (int i = startIndex; i < endIndex; i++)
- {
- if (!contains.ContainsKey(i))
- {
- Transform child = items[outOfContains[0]];
- outOfContains.RemoveAt(0);
- child.gameObject.SetActive(true);
- int row = i/maxPerLine;
- int col = i%maxPerLine;
- if (arrangement == Arrangement.Vertical)
- child.localPosition = startPos +
- new Vector2(col*itemSize.x + (col)*columuSpace,
- -row*itemSize.y - (row)*rowSpace);
- else
- child.localPosition = startPos +
- new Vector2(row*itemSize.x + (row)*columuSpace,
- -col*itemSize.y - (col)*rowSpace);
- child.gameObject.name = i.ToString();
- // Debug.LogWarning("item changed and update ");
- if (onItemRender != null){
- onItemRender(i, child);
- }
- }
- else if (forceRender)
- {
- // Debug.LogWarning("item changed and update ");
- if (onItemRender != null){
- onItemRender(i, items[contains[i]]);
- }
- }
- }
- scrollLineIndex = curLineIndex;
- }
- /// <summary>
- /// 移除当前所有
- /// </summary>
- private void ResetChildren()
- {
- items.Clear();
- for (int i = 0; i < content.childCount; i++)
- {
- Transform child = content.GetChild(i);
- child.gameObject.SetActive(false);
- }
- }
- /// <summary>
- /// 创建新节点
- /// </summary>
- /// <param name="index"></param>
- private RectTransform CreateItem(int index)
- {
- Transform child;
- // Debug.Log("create item is " + index );
- if (content.childCount > index)
- {
- child = content.GetChild(index);
- }
- else
- {
- // Debug.Log("create item item " + item.name );
- GameObject obj = GameObject.Instantiate(item) as GameObject;
- obj.transform.SetParent(content);
- obj.transform.localScale = Vector3.one;
- child = obj.transform;
- }
- child.gameObject.name = index.ToString();
- items.Add(child);
- return child as RectTransform;
- }
- /// <summary>
- /// 设置资源
- /// </summary>
- /// <param name="child"></param>
- public void SetItem(GameObject child)
- {
- if (child == null) return;
- this.item = child;
- RectTransform itemTrans = child.transform as RectTransform;
- itemTrans.pivot = new Vector2(0, 1);
- itemSize = itemTrans.sizeDelta;
- ReBuild();
- }
- /// <summary>
- /// 更新需要渲染的个数
- /// </summary>
- /// <param name="value"></param>
- public void SetChildCount(int value, bool forceRender)
- {
- if (value < 0) childCount = 0;
- else childCount = value;
- if(totalCount <= 0)//还未初始化
- return;
- if (value > items.Count && items.Count < maxPerLine * totalCount)
- {
- //当前格子数量少于应生成的数量
- int count = items.Count;
- int max = value < maxPerLine*totalCount ? value : maxPerLine*totalCount;
- for (int i = count; i < max; i++)
- {
- int row = i / maxPerLine;
- int col = i % maxPerLine;
- RectTransform child = CreateItem(i);
- if (arrangement == Arrangement.Vertical)
- child.localPosition = startPos +
- new Vector2(col * itemSize.x + (col) * columuSpace,
- -row * itemSize.y - (row) * rowSpace);
- else
- child.localPosition = startPos +
- new Vector2(row * itemSize.x + (row) * columuSpace,
- -col * itemSize.y - (col) * rowSpace);
- }
- }
- if (content == null) return;
- int rc = Mathf.CeilToInt((float) childCount/(float) maxPerLine); //设置content的大小
- if (arrangement == Arrangement.Horizontal)
- {
- content.sizeDelta = new Vector2(marginLeft + marginRight + itemSize.x*rc + columuSpace*(rc - 1),
- viewPort.y);
- if (content.sizeDelta.x > viewPort.x && content.anchoredPosition.x < viewPort.x - content.sizeDelta.x)
- content.anchoredPosition = new Vector2(viewPort.x - content.sizeDelta.x, content.anchoredPosition.y);
- }
- else
- {
- content.sizeDelta = new Vector2(viewPort.x, marginTop + marginBottom + itemSize.y*rc + rowSpace*(rc - 1));
- if (content.sizeDelta.y > viewPort.y && content.anchoredPosition.y > content.sizeDelta.y - viewPort.y)
- content.anchoredPosition = new Vector2(content.anchoredPosition.x, content.sizeDelta.y - viewPort.y);
- }
- UpdateRectItem(GetCurLineIndex(), true);
- }
- /// <summary>
- /// 添加子节点
- /// </summary>
- /// <param name="index"></param>
- public void AddChild(int index)
- {
- if (index < 0) return;
- startIndex = scrollLineIndex*maxPerLine;
- endIndex = (scrollLineIndex + totalCount)*maxPerLine;
- SetChildCount(childCount + 1, index >= startIndex && index < endIndex);
- }
- /// <summary>
- /// 删除子节点
- /// </summary>
- /// <param name="index"></param>
- public void RemoveChild(int index)
- {
- if (index < 0 || index >= childCount) return;
- startIndex = scrollLineIndex*maxPerLine;
- endIndex = (scrollLineIndex + totalCount)*maxPerLine;
- SetChildCount(childCount - 1, index >= startIndex && index < endIndex);
- }
- /// <summary>
- /// 设置显示窗口大小(现在貌似可以废弃了)
- /// </summary>
- /// <param name="port"></param>
- public void SetViewPort(Vector2 port)
- {
- if (port == viewPort) return;
- viewPort = port;
- ReBuild();
- }
- /// <summary>
- /// 设置行列最大
- /// </summary>
- /// <param name="max"></param>
- public void SetMaxPerLine(int max)
- {
- maxPerLine = max;
- ReBuild();
- }
- /// <summary>
- /// 返回顶部
- /// </summary>
- public void BackTop()
- {
- content.localPosition = Vector3.zero;
- UpdateRectItem(0, true);
- }
- /// <summary>
- /// 返回底部
- /// </summary>
- public void BackBottom()
- {
- if (arrangement == Arrangement.Vertical)
- {
- content.localPosition = new Vector3(0, -viewPort.y + content.sizeDelta.y, 0);
- }
- else
- {
- content.localPosition = new Vector3(viewPort.x - content.sizeDelta.x, 0);
- }
- UpdateRectItem(Mathf.CeilToInt((float) childCount/(float) maxPerLine) - totalCount + 1, true);
- }
- public void RefreshViewItem()
- {
- UpdateRectItem(scrollLineIndex, true);
- }
- public void SetArrangement(int arr)
- {
- arrangement = (Arrangement) arr;
- }
- public void SetHorizontal(int h)
- {
- horizontalAlign = (HorizontalAlign) h;
- }
- public void SetVerticle(int v)
- {
- verticalAlign = (VerticalAlign) v;
- }
- }
- }
|