【UGUI】无限循环列表Scroview(附加增删改查功能)

发表于2018-11-30
评论2 1.8k浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏程序行业精英群

711501594
循环列表其实是针对列表组件的优化,在项目中经常会用到,本篇中要给大家分享的是无限循环列表Scroview的使用,包括增加、删除、改动、查找等功能。

首先看一下效果  

主要功能如下:

1.可以初始化任意长度
2.可以移动到任意一个index中
3.可以动态移除当前列表的某一项数据
4.可以动态增添数据

核心代码如下:
LoopListExample
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LoopListExample : MonoBehaviour
{
    public DynamicInfinityListRenderer m_Dl;
    public Button m_BtnSetDatas;
    public Button m_BtnMove2Data;
    public Button m_BtnRemoveData;
    public Button m_BtnAddData;
    public int all_Number;
    public int moveIndex;
    public int removeIndex;
    public int addNumber;
    // Use this for initialization
    void Start () {
	    m_Dl.InitRendererList(OnSelectHandler,null);
        m_BtnSetDatas.onClick.AddListener(() =>
        {
            List<int> datas = new List<int>();
            for (int i = 0; i < all_Number; ++i)
            {
                datas.Add(i);
            }
            m_Dl.SetDataProvider(datas);
        });
	    m_BtnMove2Data.onClick.AddListener(() =>
	    {
	        if (m_Dl.GetDataProvider() != null)
	        {
                m_Dl.LocateRenderItemAtTarget(moveIndex, 1);
	        }
	        else
	        {
	            print("先设置数据吧");
            }
	    });
	    m_BtnRemoveData.onClick.AddListener(() =>
	    {
	        if (m_Dl.GetDataProvider() != null)
	        {
                if (m_Dl.GetDataProvider().Contains(removeIndex))
	            {
                    m_Dl.GetDataProvider().Remove(removeIndex);
	                m_Dl.RefreshDataProvider();
	            }
	            else
	            {
	                print("找不到数据");
	            }
            }
	        else
	        {
	            print("先设置数据吧");
	        }	                     
	    });
        m_BtnAddData.onClick.AddListener(() =>
        {
            if (m_Dl.GetDataProvider() != null)
            {
                m_Dl.GetDataProvider().Add(addNumber);
                m_Dl.RefreshDataProvider();
            }
            else
            {
                print("先设置数据吧");
            }
        });
    }
    void OnSelectHandler(DynamicInfinityItem item)
    {
        print("on select "+item.ToString());
    }
    // Update is called once per frame
    void Update () {
	}
}

DynamicInfinityListRenderer:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using System;
/// <summary>
/// 动态无限列表
/// @panhaijie
/// 2016年3月22日 10:27:51
/// </summary>
public class DynamicInfinityListRenderer : MonoBehaviour
{
    /// <summary>
    /// 单元格尺寸(宽,高)
    /// </summary>
    public Vector2 CellSize;
    /// <summary>
    /// 单元格间隙(水平,垂直)
    /// </summary>
    public Vector2 SpacingSize;
    /// <summary>
    /// 列数
    /// </summary>
    public int ColumnCount;
    /// <summary>
    /// 单元格渲染器prefab
    /// </summary>
    public GameObject RenderGO;
    /// <summary>
    /// 渲染格子数
    /// </summary>
    protected int mRendererCount;
    /// <summary>
    /// 父节点蒙版尺寸
    /// </summary>
    private Vector2 mMaskSize;
    /// <summary>
    /// 蒙版矩形
    /// </summary>
    private Rect mRectMask;
    protected ScrollRect mScrollRect;
    /// <summary>
    /// 转换器
    /// </summary>
    protected RectTransform mRectTransformContainer;
    /// <summary>
    /// 渲染脚本集合
    /// </summary>
    protected List<DynamicInfinityItem> mList_items;
    /// <summary>
    /// 渲染格子字典
    /// </summary>
    private Dictionary<int, DynamicRect> mDict_dRect;
    /// <summary>
    /// 数据提供者
    /// </summary>
    protected IList mDataProviders;
    protected bool mHasInited = false;
    /// <summary>
    /// 初始化渲染脚本
    /// </summary>
    public virtual void InitRendererList(DynamicInfinityItem.OnSelect OnSelect, DynamicInfinityItem.OnUpdateData OnUpdate)
    {
        if (mHasInited) return;
        //转换器
        mRectTransformContainer = transform as RectTransform;
        //获得蒙版尺寸
        mMaskSize = transform.parent.GetComponent<RectTransform>().sizeDelta;
        mScrollRect = transform.parent.GetComponent<ScrollRect>();
        //通过蒙版尺寸和格子尺寸计算需要的渲染器个数
        mRendererCount = ColumnCount * (Mathf.CeilToInt(mMaskSize.y / GetBlockSizeY()) + 1);
        _UpdateDynmicRects(mRendererCount);
        mList_items = new List<DynamicInfinityItem>();
        for (int i = 0; i < mRendererCount; ++i)
        {
            GameObject child = GameObject.Instantiate(RenderGO);
            child.transform.SetParent(transform);
            child.transform.localRotation = Quaternion.identity;
            child.transform.localScale = Vector3.one;
            child.layer = gameObject.layer;
            DynamicInfinityItem dfItem = child.GetComponent<DynamicInfinityItem>();
            if (dfItem == null)
                throw new Exception("Render must extend DynamicInfinityItem");
            mList_items.Add(dfItem);
            mList_items[i].DRect = mDict_dRect[i];
            mList_items[i].OnSelectHandler = OnSelect;
            mList_items[i].OnUpdateDataHandler = OnUpdate;
            child.SetActive(false);
            _UpdateChildTransformPos(child, i);
        }
        _SetListRenderSize(mRendererCount);
        mHasInited = true;
    }
    /// <summary>
    /// 设置渲染列表的尺寸
    /// 不需要public
    /// </summary>
    /// <param name="count"></param>
    void _SetListRenderSize(int count)
    {
        mRectTransformContainer.sizeDelta = new Vector2(mRectTransformContainer.sizeDelta.x, Mathf.CeilToInt((count * 1.0f / ColumnCount)) * GetBlockSizeY());
        mRectMask = new Rect(0, -mMaskSize.y, mMaskSize.x, mMaskSize.y);
        mScrollRect.vertical = mRectTransformContainer.sizeDelta.y > mMaskSize.y;
    }
    /// <summary>
    /// 更新各个渲染格子的位置
    /// </summary>
    /// <param name="child"></param>
    /// <param name="index"></param>
    void _UpdateChildTransformPos(GameObject child, int index)
    {
        int row = index / ColumnCount;
        int column = index % ColumnCount;
        Vector2 v2Pos = new Vector2();
        v2Pos.x = column * GetBlockSizeX();
        v2Pos.y = -CellSize.y - row * GetBlockSizeY();
        ((RectTransform)child.transform).anchoredPosition3D = Vector3.zero;
        ((RectTransform)child.transform).anchoredPosition = v2Pos;
    }
    /// <summary>
    /// 获得格子块尺寸
    /// </summary>
    /// <returns></returns>
    protected float GetBlockSizeY() { return CellSize.y + SpacingSize.y; }
    protected float GetBlockSizeX() { return CellSize.x + SpacingSize.x; }
    /// <summary>
    /// 更新动态渲染格
    /// 不需要public
    /// </summary>
    /// <param name="count"></param>
    void _UpdateDynmicRects(int count)
    {
        mDict_dRect = new Dictionary<int, DynamicRect>();
        for (int i = 0; i < count; ++i)
        {
            int row = i / ColumnCount;
            int column = i % ColumnCount;
            DynamicRect dRect = new DynamicRect(column * GetBlockSizeX(), -row * GetBlockSizeY() - CellSize.y, CellSize.x, CellSize.y, i);
            mDict_dRect[i] = dRect;
        }
    }
    /// <summary>
    /// 设置数据提供者
    /// </summary>
    /// <param name="datas"></param>
    public void SetDataProvider(IList datas)
    {
        _UpdateDynmicRects(datas.Count);
        _SetListRenderSize(datas.Count);
        mDataProviders = datas;
        ClearAllListRenderDr();
    }
    /// <summary>
    /// 清理可复用渲染格
    /// 不需要public
    /// </summary>
    void ClearAllListRenderDr()
    {
        if (mList_items != null)
        {
            int len = mList_items.Count;
            for (int i = 0; i < len; ++i)
            {
                DynamicInfinityItem item = mList_items[i];
                item.DRect = null;
            }
        }
    }
    /// <summary>
    /// 获得数据提供者
    /// </summary>
    /// <returns></returns>
    public IList GetDataProvider() { return mDataProviders; }
    /// <summary>
    /// 数据发生变化 供外部调用刷新列表
    /// </summary>
    [ContextMenu("RefreshDataProvider")]
    public void RefreshDataProvider()
    {
        if (mDataProviders == null)
            throw new Exception("dataProviders 为空!请先使用SetDataProvider ");
        _UpdateDynmicRects(mDataProviders.Count);
        _SetListRenderSize(mDataProviders.Count);
        ClearAllListRenderDr();
    }
    #region 移动至数据
    /// <summary>
    /// 移动列表使之能定位到给定数据的位置上
    /// </summary>
    /// <param name="target"></param>
    public virtual void LocateRenderItemAtTarget(object target, float delay)
    {
        LocateRenderItemAtIndex(mDataProviders.IndexOf(target), delay);
    }
    public virtual void LocateRenderItemAtIndex(int index, float delay)
    {
        if (index < 0 || index > mDataProviders.Count - 1)
            throw new Exception("Locate Index Error " + index);
        index = Math.Min(index, mDataProviders.Count - mRendererCount + 2);
        index = Math.Max(0, index);
        Vector2 pos = mRectTransformContainer.anchoredPosition;
        int row = index / ColumnCount;
        Vector2 v2Pos = new Vector2(pos.x, row * GetBlockSizeY());
        m_Coroutine = StartCoroutine(TweenMoveToPos(pos, v2Pos, delay));
    }
    protected IEnumerator TweenMoveToPos(Vector2 pos, Vector2 v2Pos, float delay)
    {
        bool running = true;
        float passedTime = 0f;
        while (running)
        {
            yield return new WaitForEndOfFrame();
            passedTime += Time.deltaTime;
            Vector2 vCur;
            if (passedTime >= delay)
            {
                vCur = v2Pos;
                running = false;
                StopCoroutine(m_Coroutine);
                m_Coroutine = null;
            }
            else
            {
                vCur = Vector2.Lerp(pos, v2Pos, passedTime / delay);
            }
            mRectTransformContainer.anchoredPosition = vCur;
        }
    }
    protected Coroutine m_Coroutine = null;
    #endregion
    protected void UpdateRender()
    {
        mRectMask.y = -mMaskSize.y - mRectTransformContainer.anchoredPosition.y;       
        Dictionary<int, DynamicRect> inOverlaps = new Dictionary<int, DynamicRect>();
        foreach (DynamicRect dR in mDict_dRect.Values)
        {
            if (dR.Overlaps(mRectMask))
            {
                inOverlaps.Add(dR.Index, dR);
            }
        }
        int len = mList_items.Count;
        for (int i = 0; i < len; ++i)
        {
            DynamicInfinityItem item = mList_items[i];
            if (item.DRect != null && !inOverlaps.ContainsKey(item.DRect.Index))
                item.DRect = null;
        }
        foreach (DynamicRect dR in inOverlaps.Values)
        {
            if (GetDynmicItem(dR) == null)
            {
                DynamicInfinityItem item = GetNullDynmicItem();
                item.DRect = dR;
                _UpdateChildTransformPos(item.gameObject, dR.Index);
                if (mDataProviders != null && dR.Index < mDataProviders.Count)
                {
                    item.SetData(mDataProviders[dR.Index]);
                }
            }
        }
    }
    /// <summary>
    /// 获得待渲染的渲染器
    /// </summary>
    /// <returns></returns>
    DynamicInfinityItem GetNullDynmicItem()
    {
        int len = mList_items.Count;
        for(int i=0;i< len;++i)
        {
            DynamicInfinityItem item = mList_items[i];
            if (item.DRect == null)
                return item;
        }
        throw new Exception("Error");
    }
    /// <summary>
    /// 通过动态格子获得动态渲染器
    /// </summary>
    /// <param name="rect"></param>
    /// <returns></returns>
    DynamicInfinityItem GetDynmicItem(DynamicRect rect)
    {
        int len = mList_items.Count;
        for (int i = 0; i < len; ++i)
        {
            DynamicInfinityItem item = mList_items[i];
            if (item.DRect == null)
                continue;
            if (rect.Index == item.DRect.Index)
                return item;
        }
        return null;
    }
    void Update()
    {
        if (mHasInited)
            UpdateRender();
    }
}

DynamicRect:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
/// <summary>
/// 动态格子矩形
/// </summary>
public class DynamicRect
{
    /// <summary>
    /// 矩形数据
    /// </summary>
    private Rect mRect;
    /// <summary>
    /// 格子索引
    /// </summary>
    public int Index;
    public DynamicRect(float x, float y, float width, float height, int index)
    {
        this.Index = index;
        mRect = new Rect(x, y, width, height);
    }
    /// <summary>
    /// 是否相交
    /// </summary>
    /// <param name="otherRect"></param>
    /// <returns></returns>
    public bool Overlaps(DynamicRect otherRect)
    {
        return mRect.Overlaps(otherRect.mRect);
    }
    /// <summary>
    /// 是否相交
    /// </summary>
    /// <param name="otherRect"></param>
    /// <returns></returns>
    public bool Overlaps(Rect otherRect)
    {
        return mRect.Overlaps(otherRect);
    }
    public override string ToString()
    {
        return string.Format("index:{0},x:{1},y:{2},w:{3},h:{4}", Index, mRect.x, mRect.y, mRect.width, mRect.height);
    }
}
/// <summary>
/// 动态无限渲染器
/// @panhaijie
/// </summary>
public class DynamicInfinityItem : MonoBehaviour
{
    public delegate void OnSelect(DynamicInfinityItem item);
    public delegate void OnUpdateData(DynamicInfinityItem item);
    public OnSelect OnSelectHandler;
    public OnUpdateData OnUpdateDataHandler;
    /// <summary>
    /// 动态矩形
    /// </summary>
    protected DynamicRect mDRect;
    /// <summary>
    /// 动态格子数据
    /// </summary>
    protected object mData;
    public DynamicRect DRect
    {
        set
        {
            mDRect = value;
            gameObject.SetActive(value != null);
        }
        get { return mDRect; }
    }
    void Start()
    {
    }
    /// <summary>
    /// 设置数据
    /// </summary>
    /// <param name="data"></param>
    public void SetData(object data)
    {
        if (data == null)
        {
            return;
        }
        mData = data;
        if (null != OnUpdateDataHandler)
            OnUpdateDataHandler(this);
        OnRenderer();
    }
    /// <summary>
    /// 复写
    /// </summary>
    protected virtual void OnRenderer()
    {
    }
    /// <summary>
    /// 返回数据
    /// </summary>
    /// <returns></returns>
    public object GetData()
    {
        return mData;
    }
    public T GetData<T>()
    {
        return (T)mData;
    }
}

ItemRender
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class ItemRender : DynamicInfinityItem
{
    public Text m_TxtName;
    public Button m_Btn;
// Use this for initialization
void Start () {
m_Btn.onClick.AddListener(() =>
{
            print("Click "+mData.ToString());
});
}
    protected override void OnRenderer()
    {
        base.OnRenderer();
        m_TxtName.text = mData.ToString();
    }
}

原文链接

著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台