unity scrollview 优化 高效重复利用 避免大量初始化时间过长

最近项目看到一个大佬写的比较好的动态增删的scrollview  列表,唯一有些缺陷就是无法居中, 我把他整理下分享给大家

demo https://download.csdn.net/download/weixin_41843959/10603338 

列表个数始终保持再自己设置的个数,滑动时通过把显示外看不到的子物体重新设置位置,初始化属性

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

public class UIListTest : MonoBehaviour {

    private UIHorizontal mList2;
    int[] intlist = { 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 20010, 20011, 20012, 20013, 20014, 20015, 20016, 20017, 20018, 20019 };
    void Start ()
    {
        mList2 = transform.Find("Horizontal").GetComponent<UIHorizontal>();
        //mList.Init(50, 50, 2, 3);
        mList2.InitData();
        mList2.InitList(intlist.Length, (item, index) =>
        {//子物体属性赋值
            item.name = index.ToString();
            item.Find("Text").GetComponent<Text>().text = intlist[index].ToString();
        });
    }

}

横向列表控制类

属性设置:

固定节点

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

/// <summary>
/// 横向列表
/// </summary>
public class UIHorizontal : MonoBehaviour
{
    // 外部基础参数设置  
    public int itemWidth;            //单元格宽
    public int itemHeight;           //单元格高
    public int columnCount;      // 显示列数
    public int rowCount;             // 显示行数
    public int offsetX = 0; //列间隔
    public int offsetY = 0; //行间隔

    //基础设置
    private ScrollRect scrollRect;
    private RectTransform itemParent;
    private GameObject itemObj;          //克隆体
    private Vector2 m_maskSize;     //遮罩大小

    //根据参数来计算
    private int m_createCount;            //创建的数量
    private int m_rectWidth;           //列表宽度
    private int m_rectHeigh;           //列表高度
    private int m_listCount;              //列表总的需要显示的数量,外部给
    //private int rowSum;                 //总共多少行
    private int columnSum;                 //总共多少列
    private int m_showCount;              //当前实际显示的数量(小于或等于createCount)
    private int lastStartIndex = 0;     //记录上次的初始序号
    private int m_startIndex = 0;     //显示开始序号
    private int m_endIndex = 0;           //显示结束序号
    private Dictionary<int, Transform> dic_itemIndex = new Dictionary<int, Transform>();      //item对应的序号
    private Vector3 curItemParentPos = Vector3.zero;
    private Transform m_item;

    public delegate void UpdateListItemEvent(Transform item, int index);
    private UpdateListItemEvent m_updateItem = null;

    /// <summary>
    /// 初始化数据 item长宽,列数和行 ,代码设置,如不调用界面控制
    /// </summary>
    /// <param item宽="width"></param>
    /// <param item长="heigh"></param>
    /// <param 1="column"></param>
    /// <param 一个列表最多能显示多少行(元素)="row"></param>
    public void Init(int width, int heigh, int column, int row)
    {
        itemWidth = width;
        itemHeight = heigh;
        columnCount = column;
        rowCount = row;
        //rowCount = row + 2;
        InitData();
    }

    /// <summary>
    /// 初始化列表 item长宽,列数和行 
    /// </summary>
    public void InitData()
    {
        if (columnCount >= rowCount)
        {
            columnCount = columnCount + 2;
        }
        else
        {
            rowCount = rowCount + 2;
        }
        m_createCount = columnCount * rowCount;
        if (m_createCount <= 0)
        {
            Debug.LogError("横纵不能为0!");
            return;
        }

        scrollRect = transform.GetComponent<ScrollRect>();
        if (scrollRect != null)
        {
            scrollRect.onValueChanged.AddListener(OnValueChange);
        }
        m_rectHeigh = rowCount * itemHeight;
        itemParent = transform.Find("Viewport/Content").GetComponent<RectTransform>();
        //         itemParent.anchorMin = new Vector2(0, 1);
        //         itemParent.anchorMax = new Vector2(0, 1);
        itemParent.pivot = new Vector2(0, 1);

        itemObj = transform.Find("Viewport/item").gameObject;
        itemObj.SetActive(false);
        RectTransform itemRec = itemObj.GetComponent<RectTransform>();
        itemRec.anchorMin = new Vector2(0, 1);
        itemRec.anchorMax = new Vector2(0, 1);
        itemRec.pivot = new Vector2(0, 1);

        m_maskSize = GetComponent<RectTransform>().sizeDelta;
    }

    设置元素之间的间距 spacing :SetOffset()
    public void SetOffset(int x, int y)
    {
        offsetX = x;
        offsetY = y;
        m_rectHeigh = (rowCount - 1) * (itemHeight + offsetY);
    }

    /// <summary>
    /// 刷新赋值列表 回滚到顶部
    /// </summary>
    /// <param 列表的元素的最大个数="count"></param>
    /// <param 委托:进行 单个元素的赋值 = "updateItem"></param>
    public void InitList(int count, UpdateListItemEvent updateItem)
    {
        m_listCount = count;                  //记录有多少个item
        m_updateItem = updateItem;
        itemParent.transform.localPosition = Vector2.zero;
        columnSum = count / rowCount + (count % rowCount > 0 ? 1 : 0);      //计算有多少行,用于计算出总高度
        m_rectWidth = Mathf.Max(0, columnSum * itemWidth + (columnSum - 1) * offsetX);

        itemParent.sizeDelta = new Vector2(m_rectWidth, m_rectHeigh);
        m_showCount = Mathf.Min(count, m_createCount);     //显示item的数量
        m_startIndex = 0;
        dic_itemIndex.Clear();

        for (int i = 0; i < m_showCount; i++)
        {
            Transform item = GetItem(i);
            SetItem(item, i);
        }
        ShowListCount(itemParent, m_showCount);         //显示多少个
    }

    /// <summary>
    /// 生成列表 不回滚,继续往下浏览
    /// </summary>
    /// <param 列表的元素的最大个数="count"></param>
    /// <param 委托:进行 单个元素的赋值 = "updateItem"></param>
    public void Refresh(int count, UpdateListItemEvent updateItem)
    {
        m_listCount = count;
        m_updateItem = updateItem;
        columnSum = count / rowCount + (count % rowCount > 0 ? 1 : 0);
        m_rectWidth = Mathf.Max(0, columnSum * itemWidth + (columnSum - 1) * offsetX);

        itemParent.sizeDelta = new Vector2(m_rectWidth, m_rectHeigh);
        m_showCount = Mathf.Min(count, m_createCount);     //显示item的数量
        dic_itemIndex.Clear();
        if (count == 0)
        {
            ShowListCount(itemParent, m_showCount);
            return;
        }
        //计算起始的终止序号
        //--如果数量小于遮罩正常状态下能显示的总量
        if (count <= m_createCount)
        {
            m_startIndex = 0;
            m_endIndex = count - 1;
        }
        else
        {
            m_startIndex = GetStartIndex(itemParent.localPosition.x);
            if (m_startIndex + m_createCount >= count)
            {

                m_startIndex = count - m_createCount;
                m_endIndex = count - 1;
            }
            else
            {
                m_endIndex = m_startIndex + m_createCount - 1;
            }
        }
        lastStartIndex = m_startIndex;
        if (m_endIndex < m_startIndex)
        {
            Debug.LogError("列表有问题!");
            return;
        }
        for (int i = m_startIndex; i <= m_endIndex; i++)
        {
            Transform item = GetItem(i - m_startIndex);
            SetItem(item, i);
        }
        ShowListCount(itemParent, m_showCount);
    }

    /// <summary>
    /// 创建item 有就拿来用,没有就创建
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    private Transform GetItem(int index)
    {
        Transform item = null;
        if (index < itemParent.childCount)
            item = itemParent.GetChild(index);
        else
            item = ((GameObject)GameObject.Instantiate(itemObj.gameObject)).transform;
        item.name = index.ToString();
        item.SetParent(itemParent);
        item.localScale = Vector3.one;
        return item;
    }

    /// <summary>
    /// 刷新item对应数据信息
    /// </summary>
    /// <param name="item"></param>
    /// <param name="index"></param>
    private void SetItem(Transform item, int index)
    {
        dic_itemIndex[index] = item;
        item.localPosition = GetPos(index);
        item.name = index.ToString();
        if (m_updateItem != null)
            m_updateItem(item, index);
    }

    /// <summary>
    /// item对应位置
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    private Vector2 GetPos(int index)
    {
        int spread = 0;
        return new Vector2(index / rowCount * (itemHeight + offsetX), -index % rowCount * (itemWidth + offsetY) - spread);
    }

    // 获取起始序列号
    private int GetStartIndex(float x)
    {
        x = -x;//将负坐标转正
        int _spreadWidth = 0;
        if (x <= (itemWidth + _spreadWidth))
            return 0;
        float scrollWidth = gameObject.GetComponent<RectTransform>().sizeDelta.x;
        if (x >= (itemParent.sizeDelta.x - scrollWidth - _spreadWidth))        //拉到底部了
        {
            if (m_listCount <= m_createCount)
                return 0;
            else
                return m_listCount - m_createCount;
        }
        return ((int)((x - _spreadWidth) / (itemWidth + offsetX)) + ((x - _spreadWidth) % (itemWidth + offsetX) > 0 ? 1 : 0) - 1) * rowCount;
    }

    //显示子物体的数量
    private void ShowListCount(Transform trans, int num)
    {
        if (trans.childCount < num)
            return;
        for (int i = 0; i < num; i++)
        {
            trans.GetChild(i).gameObject.SetActive(true);
        }
        for (int i = num; i < trans.childCount; i++)
        {
            trans.GetChild(i).gameObject.SetActive(false);
        }
    }

    List<int> newIndexList = new List<int>();
    List<int> changeIndexList = new List<int>();
    //列表位置刷新
    private void OnValueChange(Vector2 pos)
    {
        curItemParentPos = itemParent.localPosition;
        if (m_listCount <= m_createCount)
            return;
        m_startIndex = GetStartIndex(itemParent.localPosition.x);
        if (m_startIndex + m_createCount >= m_listCount)
        {
            m_startIndex = m_listCount - m_createCount;
            m_endIndex = m_listCount - 1;
        }
        else
        {
            m_endIndex = m_startIndex + m_createCount - 1;
        }
        if (m_startIndex == lastStartIndex)
            return;
        lastStartIndex = m_startIndex;
        newIndexList.Clear();
        changeIndexList.Clear();
        for (int i = m_startIndex; i <= m_endIndex; i++)
        {
            newIndexList.Add(i);
        }

        var e = dic_itemIndex.GetEnumerator();
        while (e.MoveNext())
        {
            int index = e.Current.Key;
            if (index >= m_startIndex && index <= m_endIndex)
            {
                if (newIndexList.Contains(index))
                    newIndexList.Remove(index);
                continue;
            }
            else
            {
                changeIndexList.Add(e.Current.Key);
            }
        }

        for (int i = 0; i < newIndexList.Count && i < changeIndexList.Count; i++)
        {
            int oldIndex = changeIndexList[i];
            int newIndex = newIndexList[i];
            if (newIndex >= 0 && newIndex < m_listCount)
            {
                m_item = dic_itemIndex[oldIndex];
                dic_itemIndex.Remove(oldIndex);
                SetItem(m_item, newIndex);
            }
        }

    }
}

 

  • 7
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值