zoukankan      html  css  js  c++  java
  • [UGUI]滑动列表优化(循环利用)

    需要注意的有下面几点:

    1.

    区分好表现上的index和逻辑上的index。表现上的index是指这个go是go列表中的第几项,但实际上这个index的意义并不大,因为在滚动的过程中go列表是轮转的;逻辑上的index是指这个go对应数据中的第几项,在滑动的过程中不断地更新逻辑上的index,然后取对应的数据去刷新显示即可。在一般的滑动列表中,有几项数据就生成几个go,因此表现上的index和逻辑上的index是一致的;而在循环利用的循环列表中,这两个是不一致的。

    那么,在实现上,就是需要知道每个go对应的逻辑index是多少了。而这个可以简化为,只需要知道第一个对应的逻辑index是多少,因为后面的就是依次递增的。

    2.

    做好缓存策略。对于循环利用的列表,需要生成的个数等于能显示的最大个数加上2-3个的缓存个数,防止滑动过快时出现穿帮。

    3.

    关于循环利用的实现。其实就是在滑动过程中,收集被移除显示的go,然后对这些go重新调整位置,并刷新go上的控件显示。那么如何收集呢,就是将go对应的逻辑index和当前的逻辑index范围进行比较,将不在这个范围内的go收集即可。然后在需要的时候取出来,刷新这些go即可。

    代码如下:

      1 using UnityEngine;
      2 using System.Collections.Generic;
      3 using System;
      4 using UnityEngine.UI;
      5 
      6 [RequireComponent(typeof(ScrollRect))]
      7 public class LoopScrollView : MonoBehaviour {
      8 
      9     private List<GameObject> goList;//当前显示的go列表
     10     private Queue<GameObject> freeGoQueue;//空闲的go队列,存放未显示的go
     11     private Dictionary<GameObject, int> goIndexDic;//key:所有的go value:真实索引
     12     private ScrollRect scrollRect;
     13     private RectTransform contentRectTra;
     14     private Vector2 scrollRectSize;
     15     private Vector2 cellSize;
     16     private int startIndex;//起始索引
     17     private int maxCount;//创建的最大数量
     18     private int createCount;//当前显示的数量
     19 
     20     private const int cacheCount = 2;//缓存数目
     21     private const int invalidStartIndex = -1;//非法的起始索引
     22 
     23     private int dataCount;
     24     private GameObject prefabGo;
     25     private Action<GameObject, int> updateCellCB;
     26     private float cellPadding;
     27 
     28     //初始化SV并刷新
     29     public void Show(int dataCount, GameObject prefabGo, Action<GameObject, int> updateCellCB, float cellPadding = 0f)
     30     {
     31         //数据和组件初始化
     32         this.dataCount = dataCount;
     33         this.prefabGo = prefabGo;
     34         this.updateCellCB = updateCellCB;
     35         this.cellPadding = cellPadding;
     36 
     37         goList = new List<GameObject>();
     38         freeGoQueue = new Queue<GameObject>();
     39         goIndexDic = new Dictionary<GameObject, int>();
     40         scrollRect = GetComponent<ScrollRect>();
     41         contentRectTra = scrollRect.content;
     42         scrollRectSize = scrollRect.GetComponent<RectTransform>().sizeDelta;
     43         cellSize = prefabGo.GetComponent<RectTransform>().sizeDelta;
     44         startIndex = 0;
     45         maxCount = GetMaxCount();
     46         createCount = 0;
     47 
     48         if (scrollRect.horizontal)
     49         {
     50             contentRectTra.anchorMin = new Vector2(0, 0);
     51             contentRectTra.anchorMax = new Vector2(0, 1);
     52         }
     53         else
     54         {
     55             contentRectTra.anchorMin = new Vector2(0, 1);
     56             contentRectTra.anchorMax = new Vector2(1, 1);
     57         }
     58         scrollRect.onValueChanged.RemoveAllListeners();
     59         scrollRect.onValueChanged.AddListener(OnValueChanged);
     60         ResetSize(dataCount);
     61     }
     62 
     63     //重置数量
     64     public void ResetSize(int dataCount)
     65     {
     66         this.dataCount = dataCount;
     67         contentRectTra.sizeDelta = GetContentSize();
     68 
     69         //回收显示的go
     70         for (int i = goList.Count - 1; i >= 0; i--)
     71         {
     72             GameObject go = goList[i];
     73             RecoverItem(go);
     74         }
     75       
     76         //创建或显示需要的go
     77         createCount = Mathf.Min(dataCount, maxCount);
     78         for (int i = 0; i < createCount; i++)
     79         {
     80             CreateItem(i);
     81         }
     82 
     83         //刷新数据
     84         startIndex = -1;
     85         contentRectTra.anchoredPosition = Vector3.zero;
     86         OnValueChanged(Vector2.zero);
     87     }
     88 
     89     //更新当前显示的列表
     90     public void UpdateList()
     91     {
     92         for (int i = 0; i < goList.Count; i++)
     93         {
     94             GameObject go = goList[i];
     95             int index = goIndexDic[go];
     96             updateCellCB(go, index);
     97         }
     98     }
     99 
    100     //创建或显示一个item
    101     private void CreateItem(int index)
    102     {
    103         GameObject go;
    104         if (freeGoQueue.Count > 0)//使用原来的
    105         {
    106             go = freeGoQueue.Dequeue();
    107             goIndexDic[go] = index;
    108             go.SetActive(true);
    109         }
    110         else//创建新的
    111         {
    112             go = Instantiate<GameObject>(prefabGo);
    113             goIndexDic.Add(go, index);
    114             go.transform.SetParent(contentRectTra.transform);
    115 
    116             RectTransform rect = go.GetComponent<RectTransform>();
    117             rect.pivot = new Vector2(0, 1);
    118             rect.anchorMin = new Vector2(0, 1);
    119             rect.anchorMax = new Vector2(0, 1);
    120         }
    121         goList.Add(go);
    122         go.transform.localPosition = GetPosition(index);
    123         updateCellCB(go, index);
    124     }
    125 
    126     //回收一个item
    127     private void RecoverItem(GameObject go)
    128     {
    129         go.SetActive(false);
    130         goList.Remove(go);
    131         freeGoQueue.Enqueue(go);
    132         goIndexDic[go] = invalidStartIndex;
    133     }
    134 
    135     //滑动回调
    136     private void OnValueChanged(Vector2 vec)
    137     {
    138         int curStartIndex = GetStartIndex();
    139         //Debug.LogWarning(curStartIndex);
    140 
    141         if ((startIndex != curStartIndex) && (curStartIndex > invalidStartIndex))
    142         {
    143             startIndex = curStartIndex;
    144 
    145             //收集被移出去的go
    146             //索引的范围:[startIndex, startIndex + createCount - 1]
    147             for (int i = goList.Count - 1; i >= 0; i--)
    148             {
    149                 GameObject go = goList[i];
    150                 int index = goIndexDic[go];
    151                 if (index < startIndex || index > (startIndex + createCount - 1))
    152                 {
    153                     RecoverItem(go);
    154                 }
    155             }
    156 
    157             //对移除出的go进行重新排列
    158             for (int i = startIndex; i < startIndex + createCount; i++)
    159             {
    160                 if (i >= dataCount)
    161                 {
    162                     break;
    163                 }
    164 
    165                 bool isExist = false;
    166                 for (int j = 0; j < goList.Count; j++)
    167                 {
    168                     GameObject go = goList[j];
    169                     int index = goIndexDic[go];
    170                     if (index == i)
    171                     {
    172                         isExist = true;
    173                         break;
    174                     }
    175                 }
    176                 if (isExist)
    177                 {
    178                     continue;
    179                 }
    180 
    181                 CreateItem(i);
    182             }
    183         }
    184     }
    185 
    186     //获取需要创建的最大prefab数目
    187     private int GetMaxCount()
    188     {
    189         if (scrollRect.horizontal)
    190         {
    191             return Mathf.CeilToInt(scrollRectSize.x / (cellSize.x + cellPadding)) + cacheCount;
    192         }
    193         else
    194         {
    195             return Mathf.CeilToInt(scrollRectSize.y / (cellSize.y + cellPadding)) + cacheCount;
    196         }
    197     }
    198 
    199     //获取起始索引
    200     private int GetStartIndex()
    201     {
    202         if (scrollRect.horizontal)
    203         {
    204             return Mathf.FloorToInt(-contentRectTra.anchoredPosition.x / (cellSize.x + cellPadding));
    205         }
    206         else
    207         {
    208             return Mathf.FloorToInt(contentRectTra.anchoredPosition.y / (cellSize.y + cellPadding));
    209         }
    210     }
    211 
    212     //获取索引所在位置
    213     private Vector3 GetPosition(int index)
    214     {
    215         if (scrollRect.horizontal)
    216         {
    217             return new Vector3(index * (cellSize.x + cellPadding), 0, 0);
    218         }
    219         else
    220         {
    221             return new Vector3(0, index * -(cellSize.y + cellPadding), 0);
    222         }
    223     }
    224 
    225     //获取内容长宽
    226     private Vector2 GetContentSize()
    227     {
    228         if (scrollRect.horizontal)
    229         {
    230             return new Vector2(cellSize.x * dataCount + cellPadding * (dataCount - 1), contentRectTra.sizeDelta.y);
    231         }
    232         else
    233         {
    234             return new Vector2(contentRectTra.sizeDelta.x, cellSize.y * dataCount + cellPadding * (dataCount - 1));
    235         }
    236     }
    237 }
     1 using UnityEngine;
     2 using System.Collections;
     3 using UnityEngine.UI;
     4 
     5 public class TestLoopScrollView : MonoBehaviour {
     6 
     7     public LoopScrollView loopScrollView;
     8     public LoopScrollView loopScrollView2;
     9     public GameObject prefabGo;
    10     public GameObject prefabGo2;
    11 
    12     private void Start ()
    13     {
    14         
    15     }
    16 
    17     private void Update()
    18     {
    19         if (Input.GetKeyDown(KeyCode.Q))
    20         {
    21             loopScrollView.Show(100, prefabGo, UpdateSV);
    22         }
    23         else if(Input.GetKeyDown(KeyCode.W))
    24         {
    25             loopScrollView.ResetSize(5);
    26         }
    27         else if (Input.GetKeyDown(KeyCode.E))
    28         {
    29             loopScrollView.ResetSize(50);
    30         }
    31         else if (Input.GetKeyDown(KeyCode.R))
    32         {
    33             loopScrollView.UpdateList();
    34         }
    35 
    36         if (Input.GetKeyDown(KeyCode.A))
    37         {
    38             loopScrollView2.Show(100, prefabGo2, UpdateSV);
    39         }
    40         else if (Input.GetKeyDown(KeyCode.S))
    41         {
    42             loopScrollView2.ResetSize(5);
    43         }
    44         else if (Input.GetKeyDown(KeyCode.D))
    45         {
    46             loopScrollView2.ResetSize(50);
    47         }
    48         else if (Input.GetKeyDown(KeyCode.F))
    49         {
    50             loopScrollView.UpdateList();
    51         }
    52     }
    53 
    54     private void UpdateSV(GameObject go, int index)
    55     {
    56         Text text = go.transform.Find("Text").GetComponent<Text>();
    57         text.text = index.ToString();
    58     }
    59 }

    效果:

  • 相关阅读:
    解决struts2在(IE,Firefox)下载文件名乱码问题
    Quartz 使用
    SpringBoot 全局异常处理器
    数据结构学习(五) Java链表实现队列
    数据结构学习(四) Java链表实现
    Linux 端口占用情况查看
    数据结构学习(三) 基于动态数组实现队列Queue
    数据结构学习(二)基于动态数组实现Stack(栈)
    SpringBoot 定时任务
    数据结构学习(一) Java中的动态数组实现
  • 原文地址:https://www.cnblogs.com/lyh916/p/8721682.html
Copyright © 2011-2022 走看看