UGUI wireless sliding list

        In game development, we often encounter situations where a large amount of data needs to be displayed, such as leaderboards, backpacks, etc. In order to optimize the display effect and performance, a common practice is to use an infinite sliding list (Infinite Scroll View). This article will analyze in detail how to implement an infinite sliding list.

Fundamental

        The basic principle of the infinite sliding list is that by dynamically loading and recycling Items, only visible Items are created and displayed when sliding, which greatly reduces memory overhead and rendering performance.

Implementation ideas

The idea of ​​implementing an infinite sliding list is as follows:

  1. Create a sliding box that contains a Content component to hold all the Items.
  2. Use the GridLayoutGroup layout component in the Content to arrange the Item according to the rules.
  3. Fixedly display a certain number of Items, and the remaining Items are dynamically created and destroyed by sliding.
  4. According to the sliding direction (horizontal or vertical), judge the sliding distance and speed. When the sliding exceeds a certain threshold, create or destroy the Item, and set its position and display content according to the data.

Main implementation steps

The main steps to realize the infinite sliding list will be introduced in detail below:

  1. Initialize Init First, we need to declare some variables and properties in the script to control the display and behavior of the sliding list. In the Init method, we need to get the components of the sliding box, set the layout, set the size of the Content, and instantiate a fixed number of Items. Here, an itemPrefab is used as the template of the Item.

  2. Handle sliding OnScroll In the OnScroll method, we need to judge whether to create or destroy an Item, and set its position and display content according to the sliding distance and speed. Here you need to do corresponding processing according to the direction of sliding (horizontal or vertical). During the sliding process, we achieve the effect of infinite sliding by changing the position and display content of the Item.

  3. Set the position and display content of the Item Through the SetPos method, we can set the position of the Item. Among them, according to the sliding direction (horizontal or vertical), set the anchor position and offset of the Item.

  4. External calls provide some external call methods for setting display content, setting the total amount of data and destroying all Items. Through the SetShow method, we can customize the display content of the Item. Through the SetTotalCount method, we can set the total number of data. Through the DestoryAll method, we can destroy all Items in order to reset the sliding list.

run instance

Code

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;

/// <summary>
/// 无限滑动列表
/// </summary>
public class InfiniteCrollView : MonoBehaviour
{
    private ScrollRect scrollRect;//滑动框组件
    private RectTransform content;//滑动框的Content
    private GridLayoutGroup layout;//布局组件

    [Header("滑动类型")]
    public ScrollType scrollType;
    [Header("固定的Item数量")]
    public int fixedCount;
    [Header("Item的预制体")]
    public GameObject itemPrefab;

    private int totalCount;//总的数据数量
    private List<RectTransform> dataList = new List<RectTransform>();//数据实体列表
    private int headIndex;//头下标
    private int tailIndex;//尾下标
    private Vector2 firstItemAnchoredPos;//第一个Item的锚点坐标

    #region Init

    /// <summary>
    /// 实例化Item
    /// </summary>
    private void InitItem()
    {
        for (int i = 0; i < fixedCount; i++)
        {
            GameObject tempItem = Instantiate(itemPrefab, content);
            dataList.Add(tempItem.GetComponent<RectTransform>());
            SetShow(tempItem.GetComponent<RectTransform>(), i);
        }
    }

    /// <summary>
    /// 设置Content大小
    /// </summary>
    private void SetContentSize()
    {
        int h;
        int v;
        if (scrollType == ScrollType.Horizontal)
        {
            h = totalCount;
            v = 1;
        }
        else
        {
            h = 1;
            v = totalCount;
        }

        content.sizeDelta = new Vector2
            (
                            layout.padding.left + layout.padding.right + h * (layout.cellSize.x + layout.spacing.x) - layout.spacing.x - transform.GetComponent<RectTransform>().rect.width+10,//10是一个调整数为了防止最后一个item显示正常可以按需调整
                layout.padding.top + layout.padding.bottom + v * (layout.cellSize.y + layout.spacing.y) - layout.spacing.y

            );
    }

    /// <summary>
    /// 设置布局
    /// </summary>
    private void SetLayout()
    {
        layout.startCorner = GridLayoutGroup.Corner.UpperLeft;
        layout.startAxis = GridLayoutGroup.Axis.Horizontal;
        layout.childAlignment = TextAnchor.UpperLeft;
        layout.constraintCount = 1;
        if (scrollType == ScrollType.Horizontal)
        {
            scrollRect.horizontal = true;
            scrollRect.vertical = false;
            layout.constraint = GridLayoutGroup.Constraint.FixedRowCount;
        }
        else if (scrollType == ScrollType.Vertical)
        {
            scrollRect.horizontal = false;
            scrollRect.vertical = true;
            layout.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
        }
    }

    /// <summary>
    /// 得到第一个数据的锚点位置
    /// </summary>
    private void GetFirstItemAnchoredPos()
    {
        firstItemAnchoredPos = new Vector2
            (
                layout.padding.left + layout.cellSize.x / 2,
                -layout.padding.top - layout.cellSize.y / 2
            );
    }

    #endregion

    #region Main

    /// <summary>
    /// 滑动中
    /// </summary>
    private void OnScroll(Vector2 v)
    {
        if (dataList.Count == 0)
        {
            Debug.LogWarning("先调用SetTotalCount方法设置数据总数量再调用Init方法进行初始化");
            return;
        }

        if (scrollType == ScrollType.Vertical)
        {
            //向上滑
            while (content.anchoredPosition.y >= layout.padding.top + (headIndex + 1) * (layout.cellSize.y + layout.spacing.y)
            && tailIndex != totalCount - 1)
            {
                //将数据列表中的第一个元素移动到最后一个
                RectTransform item = dataList[0];
                dataList.Remove(item);
                dataList.Add(item);

                //设置位置
                SetPos(item, tailIndex + 1);
                //设置显示
                SetShow(item, tailIndex + 1);

                headIndex++;
                tailIndex++;
            }
            //向下滑
            while (content.anchoredPosition.y <= layout.padding.top + headIndex * (layout.cellSize.y + layout.spacing.y)
                && headIndex != 0)
            {
                //将数据列表中的最后一个元素移动到第一个
                RectTransform item = dataList.Last();
                dataList.Remove(item);
                dataList.Insert(0, item);

                //设置位置
                SetPos(item, headIndex - 1);
                //设置显示
                SetShow(item, headIndex - 1);

                headIndex--;
                tailIndex--;
            }
        }
        else if (scrollType == ScrollType.Horizontal)
        {
            //向左滑
            while (content.anchoredPosition.x <= -layout.padding.left - (headIndex + 1) * (layout.cellSize.x + layout.spacing.x)
            && tailIndex != totalCount - 1)
            {
                //将数据列表中的第一个元素移动到最后一个
                RectTransform item = dataList[0];
                dataList.Remove(item);
                dataList.Add(item);

                //设置位置
                SetPos(item, tailIndex + 1);
                //设置显示
                SetShow(item, tailIndex + 1);

                headIndex++;
                tailIndex++;
            }
            //向右滑
            while (content.anchoredPosition.x >= -layout.padding.left - headIndex * (layout.cellSize.x + layout.spacing.x)
            && headIndex != 0)
            {
                //将数据列表中的最后一个元素移动到第一个
                RectTransform item = dataList.Last();
                dataList.Remove(item);
                dataList.Insert(0, item);

                //设置位置
                SetPos(item, headIndex - 1);
                //设置显示
                SetShow(item, headIndex - 1);

                headIndex--;
                tailIndex--;
            }
        }
    }

    #endregion

    #region Tool

    /// <summary>
    /// 设置位置
    /// </summary>
    private void SetPos(RectTransform trans, int index)
    {
        if (scrollType == ScrollType.Horizontal)
        {
            trans.anchoredPosition = new Vector2
            (
                index == 0 ? layout.padding.left + firstItemAnchoredPos.x :
                layout.padding.left + firstItemAnchoredPos.x + index * (layout.cellSize.x + layout.spacing.x),
                firstItemAnchoredPos.y
            );
        }
        else if (scrollType == ScrollType.Vertical)
        {
            trans.anchoredPosition = new Vector2
            (
                firstItemAnchoredPos.x,
                index == 0 ? -layout.padding.top + firstItemAnchoredPos.y :
                -layout.padding.top + firstItemAnchoredPos.y - index * (layout.cellSize.y + layout.spacing.y)
            );
        }
    }

    #endregion

    #region 外部调用

    /// <summary>
    /// 初始化
    /// </summary>
    public void Init()
    {
        scrollRect = GetComponent<ScrollRect>();
        content = scrollRect.content;
        layout = content.GetComponent<GridLayoutGroup>();
        scrollRect.onValueChanged.AddListener((Vector2 v) => OnScroll(v));

        //设置布局
        SetLayout();

        //设置头下标和尾下标
        headIndex = 0;
        tailIndex = fixedCount - 1;

        //设置Content大小
        SetContentSize();

        //实例化Item
        InitItem();

        //得到第一个Item的锚点位置
        GetFirstItemAnchoredPos();
    }

    /// <summary>
    /// 设置显示
    /// </summary>
    public void SetShow(RectTransform trans, int index)
    {
        //=====根据需求进行编写
        trans.GetComponentInChildren<Text>().text = index.ToString();
        trans.name = index.ToString();
    }

    /// <summary>
    /// 设置总的数据数量
    /// </summary>
    public void SetTotalCount(int count)
    {
        totalCount = count;
    }

    /// <summary>
    /// 销毁所有的元素
    /// </summary>
    public void DestoryAll()
    {
        for (int i = dataList.Count - 1; i >= 0; i--)
        {
            DestroyImmediate(dataList[i].gameObject);
        }
        dataList.Clear();
    }

    #endregion
}

/// <summary>
/// 滑动类型
/// </summary>
public enum ScrollType
{
    Horizontal,//竖直滑动
    Vertical,//水平滑动
}

        This code implements an infinite sliding list, displayed in the Unity engine. It can slide horizontally or vertically, and dynamically load and reuse Item objects during the sliding process according to the fixed number of Items, so as to achieve the effect of infinite sliding. The following will analyze the code step by step.

code analysis

First, the code defines an InfiniteScrollView class, which inherits from the MonoBehaviour class and is used to manage various functions of the sliding list. This class mainly contains the following member variables:

  1. scrollRect: The sliding frame component, used to control the sliding behavior.
  2. content: the content of the sliding frame, used to place the Item object.
  3. layout: layout component, used to control the arrangement of Item objects.
  4. scrollType: A slide type enumeration, used to specify whether the slide is horizontal or vertical.
  5. fixedCount: The fixed number of Items, used to define the number of items that can be displayed in one slide.
  6. itemPrefab: Item prefab, used to generate Item objects.

Next, the code defines some helper functions to initialize and manipulate the sliding list. These include:

  1. InitItem(): Instantiate Item, generate corresponding number of Item objects according to fixedCount, and add them to dataList.
  2. SetContentSize(): According to totalCount and layout settings, calculate and set the size of Content so that all Items can be displayed correctly.
  3. SetLayout(): Set the layout-related properties of the sliding box according to the scrollType, such as the starting corner, arrangement and constraints.
  4. GetFirstItemAnchoredPos(): Calculate the anchor point position of the first Item so that it can be displayed correctly in the sliding list.

Then, the code defines an OnScroll() function, which is used to monitor the sliding event, and handle the loading and reuse of Items during the sliding process. The specific implementation is as follows:

  1. For the vertical sliding method, when the vertical offset of the content exceeds the position of the next Item, move the first element in the dataList to the last position, and update the head and tail subscripts, as well as the moved position and display.
  2. For the horizontal sliding method, when the horizontal offset of the content exceeds the next Item position, move the first element in the dataList to the last position, and update the head and tail subscripts, as well as the moved position and display.

Finally, the code provides some functions to be called externally for initialization and setup. They include:

  1. Init(): Initialize the sliding list, and internally call the above-mentioned SetLayout, SetContentSize, InitItem and GetFirstItemAnchoredPos functions.
  2. SetShow(): Set the display content of the Item, which can be customized according to requirements.
  3. SetTotalCount(): Set the total number of items in the sliding list.
  4. DestoryAll(): Destroy all Item objects.

Through the above analysis and writing guidance, this technical blog will introduce the realization principle and specific operation steps of the infinite sliding list in as much detail as possible to help readers understand and use this function

Guess you like

Origin blog.csdn.net/Asklyw/article/details/131469465