Unity中实现带缓冲池的Listview(buffer listview)

来源:互联网 发布:java读取压缩包 编辑:程序博客网 时间:2024/06/16 17:12
  • 实测实现了一个10000条数据的Listview,sony z3真机瞬间完成初始化,滑动无比流畅。
  • 下面的截图是实际项目中使用的效果,实例化三个cellPrefab作为缓冲池,改变它们的位置和数据填充来模拟一个数据长度为15的listview:
    这里写图片描述

  • 下面直接给出完整代码,函数命名简明,希望大家能看明白:

using UnityEngine;using UnityEngine.UI;using System.Collections;using UnityEngine.EventSystems;using System;using System.Collections.Generic;using Framework;using DG.Tweening;//create by YJXpublic class AListView : LayoutGroup{    private float visualDIA;    private int eachBufferCount;    private ScrollRect scrollRect;    private RectTransform scrollRectTransform;    [HideInInspector]    private Vector2[] cellSizes;    private List<IndexAndBuffer>[] buffersArray;    private List<IndexAndPos> cellsPosInfo = new List<IndexAndPos>();    private List<int> showingCells = new List<int>();//index    public Action OnDataFillCompleted;    [SerializeField]    private ListViewAdapter _adapter;    private float width;    private float height;    public GameObject[] prefabs;    public string mainCellname;    private float[] fixedHeightArray;    private int CellsCount    {        get        {            return this.Adapter.GetCount();        }    }    public bool isManualFill = false;    public float bufferFactor = 1f;    public ListViewAdapter Adapter    {        get        {            return _adapter;        }        set        {            _adapter = value;            if (isManualFill && this.gameObject.activeInHierarchy == true && Application.isPlaying)            {                _adapter.aListview = this;                if (isManualFill)                {                    Adapter.Initialize();                    Initialize();                }            }        }    }    // Use this for initialization    protected override void Start()    {        if (this.Adapter != null && !isManualFill && Application.isPlaying)        {            Adapter.Initialize();            Initialize();        }    }    public void Initialize(Action onComplete = null)    {        if (mainCellname != string.Empty)        {            string[] difCell = mainCellname.Split(',');            for (int i = 0; i < difCell.Length; i++)            {                prefabs[i] = Resources.Load<GameObject>(difCell[i]);            }        }        this.scrollRectTransform = transform.parent.GetComponent<RectTransform>();        this.scrollRect = this.scrollRectTransform.GetComponent<ScrollRect>();        this.fixedHeightArray = new float[this.CellsCount];        this.width = this.scrollRectTransform.sizeDelta.x;        this.height = this.scrollRectTransform.sizeDelta.y;        this.cellSizes = new Vector2[this.prefabs.Length];        for (int i = 0; i < this.prefabs.Length; i++)        {            RectTransform cellRect = this.prefabs[i].GetComponent<RectTransform>();            this.cellSizes[i] = cellRect.sizeDelta;        }        if (this.scrollRect.horizontal)        {            this.visualDIA = (0.5f + this.bufferFactor) * this.width;        }        else if (this.scrollRect.vertical)        {            this.visualDIA = (0.5f + this.bufferFactor) * this.height;        }        this.eachBufferCount = this.GetEachBufferCount();        //StartCoroutine(GenerateCellBuffers());        GenerateCellBuffers();        CalculateSizeAndCellPos();        this.scrollRect.onValueChanged.AddListener(FillData);        FillData(Vector2.zero);        if (OnDataFillCompleted != null)        {            OnDataFillCompleted();        }    }    public void ReFillData()    {        ForceRecycleAllBuffers();        CalculateSizeAndCellPos();        FillData(Vector2.zero);        if (OnDataFillCompleted != null)        {            OnDataFillCompleted();        }    }    public void RefreshData()    {        ForceRecycleAllBuffers();        CalculateSizeAndCellPos(false);        FillData(Vector2.zero);    }    private int GetEachBufferCount()    {        //float scrollDistance = 0f;        float totalEachCellDistance = 0f;        if (this.scrollRect.horizontal)        {            for (int i = 0; i < this.cellSizes.Length; i++)            {                totalEachCellDistance += this.cellSizes[i].x;            }        }        else if (this.scrollRect.vertical)        {            for (int i = 0; i < this.cellSizes.Length; i++)            {                totalEachCellDistance += this.cellSizes[i].y;            }        }        int eachBuffCount = Mathf.CeilToInt((this.visualDIA * 2f) / totalEachCellDistance);        return eachBuffCount;    }    private void GenerateCellBuffers()    {        this.buffersArray = new List<IndexAndBuffer>[this.prefabs.Length];        for (int i = 0; i < this.prefabs.Length; i++)        {            this.buffersArray[i] = new List<IndexAndBuffer>();        }        /* 根据bufferFactor,协程预先生成相应个数的buffer        int showingCount = Mathf.CeilToInt(this.eachBufferCount / ((0.5f + this.bufferFactor)));        showingCount = showingCount >= this.eachBufferCount ? this.eachBufferCount - 1 : showingCount;        for (int i = 0; i < this.eachBufferCount; i++)        {            for (int j = 0; j < this.prefabs.Length; j++)            {                GameObject go = GameObject.Instantiate<GameObject>(this.prefabs[j]) as GameObject;                RectTransform rect = go.GetComponent<RectTransform>();                rect.SetParent(transform);                rect.localScale = Vector3.one;                go.SetActive(false);                this.buffersArray[j].Add(new IndexAndBuffer(-1, go, false));            }            //if (i >= showingCount)            //{            //    this.scrollRect.onValueChanged.Invoke(Vector2.zero);            //}            //yield return null;        }        this.scrollRect.onValueChanged.Invoke(Vector2.zero);        yield return null;         * */    }    public float CalculateSizeAndCellPos(bool isGotoBegin = true)    {        this.cellsPosInfo.Clear();        float length = 0;        float offset = 0;        if (this.scrollRect.vertical)        {            for (int i = 0; i < this.CellsCount; i++)            {                int prefabIndex = this.Adapter.GetCellPrefabIndex(i);                length += this.cellSizes[prefabIndex].y;            }            RectTransform rt = this.transform.parent.GetComponent<RectTransform>();            if (length < rt.sizeDelta.y)            {                length = rt.sizeDelta.y;            }            this.rectTransform.sizeDelta = new Vector2(this.rectTransform.sizeDelta.x, length);            if (isGotoBegin == true)            {                this.transform.localPosition = new Vector3(0.0f, -(length / 2.0f - rt.sizeDelta.y / 2.0f), 0.0f);            }            for (int i = 0; i < this.CellsCount; i++)            {                int prefabIndex = this.Adapter.GetCellPrefabIndex(i);                float cellHeight = this.cellSizes[prefabIndex].y;                Vector3 localPos = new Vector3(0.0f, (length / 2.0f - offset - cellHeight / 2.0f), 0.0f);                this.cellsPosInfo.Add(new IndexAndPos(i, localPos));                offset += cellHeight;            }        }        else if (this.scrollRect.horizontal)        {            for (int i = 0; i < this.CellsCount; i++)            {                int prefabIndex = this.Adapter.GetCellPrefabIndex(i);                length += this.cellSizes[prefabIndex].x;            }            RectTransform rt = this.transform.parent.GetComponent<RectTransform>();            if (length < rt.sizeDelta.x)            {                length = rt.sizeDelta.x;            }            this.rectTransform.sizeDelta = new Vector2(length, this.rectTransform.sizeDelta.y);            if (isGotoBegin == true)            {                this.transform.localPosition = new Vector3(-(length / 2.0f - rt.sizeDelta.y / 2.0f), 0.0f, 0.0f);            }            for (int i = 0; i < this.CellsCount; i++)            {                int prefabIndex = this.Adapter.GetCellPrefabIndex(i);                float cellWidth = this.cellSizes[prefabIndex].x;                Vector3 localPos = new Vector3((length / 2.0f - offset - cellWidth / 2.0f), 0.0f, 0.0f);                this.cellsPosInfo.Add(new IndexAndPos(i, localPos));                offset += cellWidth;            }        }        return length;    }    private void FillData(Vector2 vec2)    {        RecycleBuffers();        if (this.scrollRect.horizontal)        {            float currentPos = -this.rectTransform.localPosition.x;            for (int i = 0; i < this.cellsPosInfo.Count; i++)            {                IndexAndPos indexAndPos = cellsPosInfo[i];                float distance2Mid = Mathf.Abs(indexAndPos.localPos.x - currentPos);                if (distance2Mid <= this.visualDIA /** this.width*/ && !this.showingCells.Contains(indexAndPos.mainIndex))                {                    IndexAndBuffer buffer = GetUsableBuffer(indexAndPos.mainIndex);                    if (buffer != null)                    {                        buffer.isUsing = true;                        buffer.mainIndex = indexAndPos.mainIndex;                        buffer.bufferGo.SetActive(true);                        buffer.bufferGo.transform.localPosition = indexAndPos.localPos;                        buffer.bufferGo.name = indexAndPos.mainIndex.ToString();                        this.Adapter.FillItemData(buffer.bufferGo, indexAndPos.mainIndex);                    }                }            }        }        else if (this.scrollRect.vertical)        {            float currentPos = -this.rectTransform.localPosition.y;            for (int i = 0; i < this.cellsPosInfo.Count; i++)            {                IndexAndPos indexAndPos = cellsPosInfo[i];                float distance2Mid = Mathf.Abs(indexAndPos.localPos.y - currentPos);                if (distance2Mid <= this.visualDIA /** this.width*/ && !this.showingCells.Contains(indexAndPos.mainIndex))                {                    IndexAndBuffer buffer = GetUsableBuffer(indexAndPos.mainIndex);                    if (buffer != null)                    {                        buffer.isUsing = true;                        buffer.mainIndex = indexAndPos.mainIndex;                        buffer.bufferGo.SetActive(true);                        buffer.bufferGo.transform.localPosition = indexAndPos.localPos;                        buffer.bufferGo.name = indexAndPos.mainIndex.ToString();                        this.Adapter.FillItemData(buffer.bufferGo, indexAndPos.mainIndex);                    }                }            }        }    }    private IndexAndBuffer GetUsableBuffer(int index)    {        int prefabIndex = this.Adapter.GetCellPrefabIndex(index);        List<IndexAndBuffer> buffers = this.buffersArray[prefabIndex];        for (int i = 0; i < buffers.Count; i++)        {            if (!buffers[i].isUsing)            {                this.showingCells.Add(index);                return buffers[i];            }        }        GameObject go = GameObject.Instantiate<GameObject>(this.prefabs[prefabIndex]) as GameObject;        RectTransform rect = go.GetComponent<RectTransform>();        rect.SetParent(transform);        rect.localScale = Vector3.one;        go.SetActive(false);        IndexAndBuffer buffer = new IndexAndBuffer(-1, go, false);        this.buffersArray[prefabIndex].Add(buffer);        this.showingCells.Add(index);        return buffer;    }    private void ForceRecycleAllBuffers()    {        for (int i = 0; i < this.buffersArray.Length; i++)        {            List<IndexAndBuffer> buffers = this.buffersArray[i];            for (int j = 0; j < buffers.Count; j++)            {                IndexAndBuffer buffer = buffers[j];                buffer.isUsing = false;                buffer.bufferGo.SetActive(false);                this.showingCells.Remove(buffer.mainIndex);            }        }    }    private void RecycleBuffers()    {        if (this.scrollRect.horizontal)        {            float currentPos = -this.rectTransform.localPosition.x;            for (int i = 0; i < this.buffersArray.Length; i++)            {                List<IndexAndBuffer> buffers = this.buffersArray[i];                for (int j = 0; j < buffers.Count; j++)                {                    IndexAndBuffer buffer = buffers[j];                    float distance2Mid = Mathf.Abs(buffer.bufferGo.transform.localPosition.x - currentPos);                    if (buffer.isUsing && distance2Mid > this.visualDIA)                    {                        buffer.isUsing = false;                        buffer.bufferGo.SetActive(false);                        this.showingCells.Remove(buffer.mainIndex);                    }                }            }        }        else if (this.scrollRect.vertical)        {            float currentPos = -this.rectTransform.localPosition.y;            for (int i = 0; i < this.buffersArray.Length; i++)            {                List<IndexAndBuffer> buffers = this.buffersArray[i];                for (int j = 0; j < buffers.Count; j++)                {                    IndexAndBuffer buffer = buffers[j];                    float distance2Mid = Mathf.Abs(buffer.bufferGo.transform.localPosition.y - currentPos);                    if (buffer.isUsing && distance2Mid > this.visualDIA)                    {                        buffer.isUsing = false;                        buffer.bufferGo.SetActive(false);                        this.showingCells.Remove(buffer.mainIndex);                    }                }            }        }    }public void GotoIndex(int index, float duration = 0.5f)    {        if (index > this.CellsCount)        {            ADebug.LogError("越界 越界 越界 越界 越界 越界 越界 越界 越界 越界 ");            return;        }        if (this.scrollRect.vertical)        {            float height = 0;            for (int i = 0; i < index; i++)            {                int prefabIndex = this.Adapter.GetCellPrefabIndex(i);                height += this.cellSizes[prefabIndex].y;            }            RectTransform rt = this.transform.parent.GetComponent<RectTransform>();            if (height < rt.sizeDelta.y)            {                height = rt.sizeDelta.y;            }            height = (this.rectTransform.sizeDelta.y - rt.sizeDelta.y) / 2 - height;            this.transform.DOLocalMoveY(-height, duration);        }        else if (this.scrollRect.horizontal)        {            float width = 0;            for (int i = 0; i < index; i++)            {                int prefabIndex = this.Adapter.GetCellPrefabIndex(i);                width += this.cellSizes[prefabIndex].x;            }            RectTransform rt = this.transform.parent.GetComponent<RectTransform>();            if (width < rt.sizeDelta.x)            {                width = rt.sizeDelta.x;            }            width = (this.rectTransform.sizeDelta.x - rt.sizeDelta.x) / 2 - width;// -base.spacing.y * (index - 1);            this.transform.DOLocalMoveX(-width, duration);            CalculateLayoutInputHorizontal();        }    }    #region override    public override void CalculateLayoutInputVertical()    {        //Debug.Log("CalculateLayoutInputVertical");    }    public override void SetLayoutHorizontal()    {        //Debug.Log("SetLayoutHorizontal");    }    public override void SetLayoutVertical()    {        //Debug.Log("SetLayoutVertical");    }    #endregion}public class IndexAndPos        //for each cell{    public int mainIndex;    public Vector3 localPos;    public Vector2 cellSize;    public IndexAndPos(int index, Vector3 localPos)    {        this.mainIndex = index;        this.localPos = localPos;    }}public class IndexAndBuffer{    public int mainIndex;    public GameObject bufferGo;    public bool isUsing;    public IndexAndBuffer(int index, GameObject buffer, bool isUsing)    {        this.mainIndex = index;        this.bufferGo = buffer;        this.isUsing = isUsing;    }}
  • 数据填充器:
public abstract class ListViewAdapter : MonoBehaviour    {        [HideInInspector]        public AListView aListview;        public virtual void Initialize()        {        }        public abstract int GetCount();        public abstract void FillItemData(GameObject item, int cellindex);        public virtual int GetCellPrefabIndex(int index)        {            return 0;        }}
原创粉丝点击