抽空做了一个UGUI的无限滚动的效果。只做了一半(向下无限滚动)。网上也看了很多教程,感觉还是按照自己的思路来写可能比较好。搭建如下:
content节点不添加任何组件。布局组件默认是会重新排版子节点的,所以如果子节点的位置变化,会重新排版,不能达到效果。Size Fitter组件也不加,自己写代码调整Size大小(不调整大小,无法滑动)。
最主要的实现过程就是用Queue来搬运Cell。在向下滚动的过程中(鼠标上滑),顶部滑出View Port的Cell被搬运到底部续上。这点类似于Queue的先见先出原则,再把Dequeue出来的元素添加到末尾,就很类似于ScrollView的无限滚动的原理了。在鼠标上滑的过程中,content的PosY值是一直增加的,所以触发滚动的条件就可以设定为位移之差大于Cell的高度值即可。
数据的刷新,数据到头之后,不能再次进行滚动轮换了,这里用一组值来记录初始化的一组Cell显示的是数据的哪一段。例如HeadNum和TaiNum。比如用20个Cell显示100条数据。初始化后,HeadNum就是0,TailNum就是19。上滑一行数据后,HeadNum=4,TailNum=23(这里假设是20个Cell排成4列)。
下面是完整代码:
public class UIScrollViewTest : MonoBehaviour { public RectTransform content; public GameObject cell; // cell的初始化个数 public int cellAmount = 0; // 鼠标上滑时,存储Cell的Queue。正序存储 public Queue F_cellQuee = new Queue(); // 鼠标下滑时,存储Cell的Queue。到序存储 public Queue B_cellQuee = new Queue(); // cell的Size public Vector2 cellSize = new Vector2(100,100); // cell的间隔 public Vector2 cellOffset = new Vector2(0,0); // 列数 public int columnCount = 0; private int rowCount; // 上一次content的位置 public float lastPos; // 滚动的次数 public int loopCount = 0; // cell显示的数据段的开头和结尾序号 public int HeadNum = 0; public int TailNum; public Sprite[] sp; public List<Sprite> data; void Start() { for (int i = 0; i < sp.Length; i++) { data.Add(sp[i]); } InitialScrollView(data); TailNum = cellAmount-1; lastPos = content.localPosition.y; //Debug.LogError("行数是:::" + rowCount); //Debug.LogError("+++++++++++++++++ " + (5>>3)); } void Update() { // 触发滚动。 if (content.localPosition.y - lastPos > cellSize.y && data.Count - cellAmount - loopCount*columnCount >0) { //Debug.LogError("11111111111 " + (data.Count - cellAmount - loopCount * columnCount)); LoopScrolView(data); lastPos = content.localPosition.y; } } // 初始化cell void InitialScrollView(List<Sprite> data) { for (int i = 0; i < cellAmount; i++) { GameObject obj = Instantiate(cell.gameObject); obj.transform.SetParent(content); obj.name = "cell0" + i.ToString(); obj.transform.GetChild(0).GetComponent<Text>().text = "cell0"+i.ToString(); // 显示默认的数据 obj.GetComponent<Image>().sprite = data[i]; } // 初始化Queue for (int i = content.childCount-1; i >= 0; i--) { B_cellQuee.Enqueue(content.GetChild(i).gameObject); } for (int i = 0; i < content.childCount; i++) { F_cellQuee.Enqueue(content.GetChild(i).gameObject); } // 计算行数 if (cellAmount % columnCount >0) { rowCount = cellAmount / columnCount + 1; } else { rowCount = cellAmount / columnCount; } // 排列cell的位置 int index = 0; for (int r = 1; r <= rowCount; r++) { for (int c = 1; c <= columnCount; c++) { if (index < cellAmount) { Vector2 pos = new Vector2(cellSize.x / 2 + (cellSize.x + cellOffset.x) * (c-1), -cellSize.y / 2 - (cellOffset.y + cellSize.y) * (r-1)); content.GetChild(index).GetComponent<RectTransform>().SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top, 0, 100); content.GetChild(index).GetComponent<RectTransform>().SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, 0, 100); content.GetChild(index).GetComponent<RectTransform>().anchoredPosition = pos; index++; } } } Vector2 v = content.sizeDelta; // 初始化content的size content.sizeDelta = new Vector2(v.x, rowCount * cellSize.y + cellOffset.y*(rowCount-1)); } /// 保持content的大小,这里是保持大小为在cell的行数基础上,向下多出bottomCount行的距离 void SetContentSize(int upperCount, int bottomCount) { if (content.sizeDelta != new Vector2(content.sizeDelta.x, content.sizeDelta.y + bottomCount * (cellSize.y + cellOffset.y))) { content.sizeDelta = new Vector2(content.sizeDelta.x, content.sizeDelta.y + bottomCount*(cellSize.y + cellOffset.y)); } } // 计算顶部的Cell轮换到底部时的位置。以当前最后一行的最后一个Cell的位置为基准计算。 void SetBottomCellPosition(int index, RectTransform rect, Vector2 pos) { Vector2 v = Vector2.zero; if (cellAmount % columnCount == 0) // 整除。每一行都满的情况。 { float x = pos.x - cellSize.x * (columnCount - index-1) - cellOffset.x * (columnCount-index-1); float y = pos.y - cellSize.y - cellOffset.y; v = new Vector2(x,y); } // 出现不满行的情况。例如数据有103个,可以用23个cell来轮换。这样就会出现不满行的情况。 // 这种情况下是顶部的一行cell顺次接到底部不满的行。例如23号cell后面接1号和2号cell,3号和4号cell填充到第“7”行 else if (cellAmount % columnCount + index+1<=columnCount) { float x = pos.x + cellSize.x * (index+1) + cellOffset.x * (index+1); float y = pos.y; v = new Vector2(x, y); } else { float x = pos.x - cellSize.x * (columnCount - index-1) - cellOffset.x * (columnCount - index-1); float y = pos.y - cellSize.y - cellOffset.y; v = new Vector2(x, y); } //Debug.LogError("++++++++++++++ " + pos+ " "+ v); rect.anchoredPosition = v; rect.SetAsLastSibling(); } // 计算底部的cell轮换到顶部是的位置,基准位置是当前行的第一个cell。 void SetUpperCellPosition(int index, RectTransform rect, Vector2 pos) { Vector2 v = Vector2.zero; if (cellAmount % columnCount == 0) // 整除 { float x = pos.x + cellSize.x * index + cellOffset.x * index; float y = pos.y + cellSize.y + cellOffset.y; v = new Vector2(x, y); } //else if (cellAmount % columnCount + index + 1 <= columnCount) //{ // float x = pos.x + cellSize.x * (index + 1) + cellOffset.x * (index + 1); // float y = pos.y; // v = new Vector2(x, y); //} //else //{ // float x = pos.x - cellSize.x * (columnCount - index - 1) - cellOffset.x * (columnCount - index - 1); // float y = pos.y - cellSize.y - cellOffset.y; // v = new Vector2(x, y); //} //Debug.LogError("++++++++++++++ " + pos+ " "+ v); rect.anchoredPosition = v; rect.SetAsFirstSibling(); } // 鼠标上滑时,显示当前cell的数据。同时记录数据段的序号递增。 void ShowRestCellData(Image cell, int index) { if (TailNum< data.Count-1) { Debug.LogError("当前的序号是::::" + TailNum); TailNum++; HeadNum++; cell.sprite = data[TailNum]; } } void ShowPreviousCellData(Image cell, int index) { if (HeadNum > 0) { Debug.LogError("当前的序号是::::" + HeadNum); TailNum--; HeadNum--; cell.sprite = data[HeadNum]; } } // 轮换的函数。每次乱换一行的cell。 void LoopScrolView(List<Sprite> data) { SetContentSize(0, 1); loopCount++; RectTransform rect2 = content.GetChild(content.childCount - 1).GetComponent<RectTransform>(); for (int i = 0; i < columnCount; i++) { GameObject obj = F_cellQuee.Dequeue() as GameObject; RectTransform rect = obj.GetComponent<RectTransform>(); ShowRestCellData(obj.GetComponent<Image>(), i); SetBottomCellPosition(i, rect, rect2.anchoredPosition); F_cellQuee.Enqueue(obj); } } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持亿速云。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。