建设营销网站多少钱,注册网络科技公司需要什么条件,本地网站搭建,罗湖网站建设优化前言#xff1a;
自己之前其实比着书上实现过一个循环滑动列表#xff0c;并且商业化到了项目里#xff0c;上线后也在用。可后来怎么也想不起来细节#xff0c;看着之前的代码也看不很懂。这次复习一下#xff0c;希望真能理解它的本质#xff0c;也记录一下#xff0…前言
自己之前其实比着书上实现过一个循环滑动列表并且商业化到了项目里上线后也在用。可后来怎么也想不起来细节看着之前的代码也看不很懂。这次复习一下希望真能理解它的本质也记录一下分享给所有需要的同学。
我们看源代码之所以看不懂是因为没有理解它想干什么只有理解了一段代码的思想才能够顺着代码去读懂它的本意。要分清楚什么是主要的什么是附加的就先弄明白一件事它到底要做一件什么事。
为什么需要循环列表很多面试在问项目中也在用但其实很多项目可能并没有真正遇到过这个问题。一般来说一个循环列表里元素在一百个以下是没有什么问题的在真机上也不会卡直接把所有元素初始化然后塞到ScrollRect的content里就完事了简单好用还容易扩展。但我之前的项目一个scrollrect里要塞500个元素每个元素里有几张图片几个文本性能就扛不住了一滑动就卡的不行那种情况下才真正需要用到循环滑动列表。
其实循环滑动列表也不是唯一的选择很多rpg根本就不做这个。而是用分页的方式显示背包元素配合展示界面。所以最开始还是弄清需求弄清数据规模然后再进行技术选型。
思路
之所以直接塞太多元素会卡顿本质上还是因为里边的元素的可见性都是true的只是通过ScrollRect的Mask把它们裁剪掉了。而通过循环列表不可见的元素并不会参与运算所以性能会得到较大提高。所以核心思路还是计算哪些元素可见哪些元素不可见。
目前主流的做法都是让Content的Size等于真实的Size也就是说你有100个元素那content的大小就是100个元素布局后的宽和高。只是里边的元素并不都显示我这里说的也是依赖于ScrollRect做的一种方法。因为自带的ScrollRect可以方便的帮你处理好回弹。如果是自己不依赖于ScrollRect去通过监听鼠标滑动来实现会麻烦很多。
所以核心要解决的问题就是计算剔除但其中又分为两部分第一部分是初始化第二部分是滑动的处理。两部分一起思考复杂度会高很多因此我先推荐一种简单的思路理解后自己举一反三即可。
我们首先要做的一件事就是记录可视区域。可视区域可以用一个Rect结构来存储。可视区域指哪呢一般就是指ViewPort或者ScrollRect的大小本身。在这个矩形内的元素可见不在这个矩形内的元素不可见。这里请注意如果你的可视区域始终定义为(0, 0, width, height)那你里边的元素在滑动时坐标就需要配合Content的坐标做偏移。如果你的可视区域是相对于content的左上角来计算的那滑动过程中指定index的元素的坐标是不会变的但可视区域的起始坐标就一直在变。这点注意好即可。
然后我说一下最简单的理解核心算法的方法 不考虑效率。首先假设你的滑动列表里要显示100个元素。那么你直接从0到100去遍历每个元素的位置是不是可以算出来这个相对简单知道每行每列有多少个元素可以走设置也可以自动计算那么指定的index位置你就可以算出来然后看这个元素的rect和可视区域是否有交集有的话这个元素就需要被显示否则就需要隐藏。显示的元素你从一个池子里去拿不需要显示的元素你给它塞回池子里。这不就可以了最开始不建议思考什么滑动的时候下边的元素弄到上边去不要这样理解会增加复杂度而是不可见的元素塞到池子里。可见的元素从池子里拿。
其实根据上边的思想最简单的代码实现就有了后边所处理的细节都是和外部系统进行沟通以及算法优化而已。因为比如上述算法明显有一个缺点就是每次滑动关心的元素太多可见的元素在滑动过程中并没有离开可视界面如果也从池子里从新拿一份出来效率太差。所以一般第一步优化就是记录哪些已经是可见的元素在滑动过程中那些仍然可见的就保留。不可见的塞回池子然后除了之前可见的元素外新变的可见的再从池子里拿即可。然后就是滑动的方向性。从下往上滑动的时候小的index会变的不可见大的index会变得可见因此一部分元素是不需要计算的等等。
说的不是很清楚反正先记录一下吧后续再优化有想交流的可以私信给我。后续可能考虑做个视频讲解实现我不太擅长写图文并茂的文章写文章功底还差得远。
贴代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;public class CircleScrollView : MonoBehaviour
{public float ItemWidth 100;public float ItemHeight 100;public float SpaceX 10;public float SpaceY 10;public CircleScrollItemBase ItemPrefab;private float ViewWidth;private float ViewHeight;private int ColNum;private int RowNum;private Rect VisibleRect;private ScrollRect _scroll;public ScrollRect Scroll{get{if (_scroll null){_scroll GetComponentScrollRect();}return _scroll;}}void Awake(){InitSet();}void InitSet(){Rect rect GetComponentRectTransform().rect;ViewWidth rect.width;ViewHeight rect.height;ColNum (int)((ViewWidth SpaceX) / (ItemWidth SpaceX));Scroll.vertical true;Scroll.horizontal false;VisibleRect new Rect(0, 0, ViewWidth, ViewHeight);_list new ListCircleScrollItemBase();_pool new StackCircleScrollItemBase();Scroll.onValueChanged.AddListener(OnScroll);}[NonSerialized]public int DataCount 0;private int VisibleCount 0;public void SetData(Listint datas){SetData(datas.Count);}public void SetData(int count){DataCount count;CalContentSize();CalItemCount();FillItems();_lastPosition Scroll.content.anchoredPosition;}private void CalItemCount(){int rowNum (int)(Math.Ceiling(ViewHeight SpaceY) / (ItemHeight SpaceY));VisibleCount (rowNum 1) * ColNum;}public void FillItems(){//至少保证if (_list.Count VisibleCount){int diff VisibleCount - _list.Count;for (int i 0; i diff; i){_list.Add(GetFromPool());}}for (int i 0; i _list.Count; i){int startRow i / ColNum;int startCol i % ColNum;if (i DataCount){_list[i].gameObject.SetActive(true);_list[i].Index i;_list[i].Refresh(i);_list[i].LeftTop new Vector2(startCol * (SpaceX ItemWidth), -startRow * (SpaceY ItemHeight));}}}private ListCircleScrollItemBase _list;private StackCircleScrollItemBase _pool;private void CalContentSize(){RowNum (int) Mathf.Ceil((float)DataCount / ColNum);Vector2 vec new Vector2(ColNum * ItemWidth (ColNum - 1) * SpaceX,RowNum * ItemHeight (RowNum - 1) * SpaceY);vec.x Math.Max(vec.x, ViewWidth);vec.y Math.Max(vec.y, ViewHeight);Scroll.content.sizeDelta vec;Scroll.content.pivot new Vector2(0, 1);Scroll.content.anchorMin new Vector2(0, 1);Scroll.content.anchorMax new Vector2(0, 1);Scroll.content.anchoredPosition Vector2.zero;}private CircleScrollItemBase GetFromPool(){if (_pool.Count 0){return _pool.Pop();}else{GameObject go InstantiateGameObject(ItemPrefab.gameObject, Scroll.content, false);go.SetActive(false);return go.GetComponentCircleScrollItemBase();}}private void ReleaseItem(CircleScrollItemBase item){item.gameObject.SetActive(false);item.Reset();_pool.Push(item);}private Vector2 _lastPosition;private Rect GetGridRect(int index){int startRow index / ColNum;int startCol index % ColNum;Vector2 startPos new Vector2(startCol * (SpaceX ItemWidth), startRow * (SpaceY ItemHeight)) - Scroll.content.anchoredPosition;Rect rect new Rect(startPos.x, startPos.y, ItemWidth, ItemHeight);return rect;}private void OnScroll(Vector2 delta){for (int i _list.Count - 1; i 0 ; --i){Rect rect GetGridRect(_list[i].Index);if (!Visible(rect)){ReleaseItem(_list[i]);_list.RemoveAt(i);}else{_list[i].gameObject.SetActive(true);}}if (_list.Count 0){for (int i 0; i DataCount; i){Rect rect GetGridRect(i);if (Visible(rect)){CircleScrollItemBase item AppendItem(i);_list.Add(item);}}}else{int index _list[0].Index - 1;int afterIndex _list[_list.Count - 1].Index 1;while (index 0){Rect rect GetGridRect(index);if (!Visible(rect))break;CircleScrollItemBase item AppendItem(index);index--;_list.Insert(0, item);}while (afterIndex DataCount){Rect rect GetGridRect(afterIndex);if (!Visible(rect))break;CircleScrollItemBase item AppendItem(afterIndex);afterIndex;_list.Add(item);}}}private CircleScrollItemBase AppendItem(int index){int startRow index / ColNum;int startCol index % ColNum;CircleScrollItemBase item GetFromPool();item.gameObject.SetActive(true);item.Index index;item.Refresh(index);item.LeftTop new Vector2(startCol * (SpaceX ItemWidth), -startRow * (SpaceY ItemHeight));return item;}private bool Visible(Rect rect){return rect.Overlaps(VisibleRect);}
}翻译 搜索 复制