Componente de lista ListView personalizado de UGUI (lista virtual)

Recientemente intenté usar ugui y descubrí que es realmente inconveniente, muchos de ellos son componentes básicos. El ScrollRect incorporado no tiene una lista virtual, si hay 10,000, ¿no sería necesario crear 10,000 subobjetos?
Sin embargo, como programador, ¿cómo no ser vago? Planeo encapsular un componente yo mismo:
Efecto:
Insertar descripción de la imagen aquí
el propósito generalmente se usa para hacer clasificaciones, mostrar listas, etc.: de esta manera,
Insertar descripción de la imagen aquí
primero necesita una estructura de datos para almacenar datos más tarde:

public struct ItemDate
{
    public int index;
    public object data;
    public UIList m_parent;
}

El elemento correspondiente a la lista es obligatorio:
el elemento también tiene una máscara de interfaz de usuario

public class Item:MonoBehaviour
{
    public int index;
    public object data;
    public UIList m_parent;
    public Dictionary<string, Transform> m_childName;
    public void refData()
    {
        index = 0;
        data = null;
        m_parent = null;
        m_childName = new Dictionary<string, Transform>();
    }
}

Solo hice las funciones: distancia desde arriba, distancia desde abajo, distancia desde la izquierda, espaciado de lista, obtención de la altura de medición del texto, devolución de llamada cuando cambia el ancho y alto de la vista de lista, devolución de llamada cuando se actualizan los datos.
Por supuesto, puede expandirlo según sus propias necesidades ~
Por conveniencia, guardé todos los miembros del elemento y simplemente llamé al par clave-valor del diccionario directo para obtenerlo. Sin embargo, no hay distinción entre tipos. por lo que necesita obtener el Componente <Componente> usted mismo.

 /// <summary>
 /// 储存ui成员变量
 /// </summary>
 /// <param name="skin"></param>
 /// <returns></returns>
 private Dictionary<string, Transform> setUIChildName(Transform skin)
 {
     Dictionary<string, Transform> map = new Dictionary<string, Transform>();
     for (int i = 0; i < skin.childCount; i++)
     {
         Transform childobj = skin.GetChild(i);
         map[childobj.gameObject.name] = childobj;
         if (childobj.childCount > 0)
         {
             setUIChildName(childobj);
         }
     }
     return map;
 }

El deslizamiento cambia las coordenadas del elemento actual y actualiza los datos: uso contadores: startindex y endindex para determinar el intervalo,
luego es para distinguir si se está deslizando hacia arriba o hacia abajo:
al principio, mi idea era registrar el cuadro actual y el fotograma anterior La dirección se juzga por la diferencia entre ellos, startindex y endindex se actualizan y finalmente aparece bugQAQ. .
Luego juzgue en función de la altura excedente del borde rectangular, obtenga cur, compárelo con startindex, si es mayor que él, es la dirección hacia arriba, de lo contrario es la dirección hacia abajo y luego actualiza el contador. Código completo
Insertar descripción de la imagen aquí
:

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

public class Item:MonoBehaviour
{
    public int index;
    public object data;
    public UIList m_parent;
    public Dictionary<string, Transform> m_childName;
    public void refData()
    {
        index = 0;
        data = null;
        m_parent = null;
        m_childName = new Dictionary<string, Transform>();
    }
}
public struct ItemDate
{
    public int index;
    public object data;
    public UIList m_parent;
}
public class UIList : ScrollRect
{
    RectTransform rectTr;

    private float m_width;
    private float m_height;
    private List<ItemDate> m_datas;
    private RectTransform m_Skin;
    private List<RectTransform> comList;
    private int startIndex;
    private int endIndex;
    /// <summary>
    /// 宽高改变时触发
    /// </summary>
    public event Action OnChangeViewWH;
    /// <summary>
    /// 数据跟新时调用
    /// </summary>
    public event Action<Item> OnUpdateItem;
    /// <summary>
    /// 距离顶部
    /// </summary>
    private float _m_top;
    public float m_top {
        set { _m_top = value; }
    }
    /// <summary>
    /// 距离底部
    /// </summary>
    private float _m_down;
    public float m_down
    {
        set { _m_down = value; }
    }
    /// <summary>
    /// 距离左部
    /// </summary>
    private float _m_left;
    public float m_left
    {
        set { _m_left = value; }
    }
    /// <summary>
    /// 间距
    /// </summary>
    private float _Spacing;
    public float Spacing
    {
        get { return _Spacing; }
        set { _Spacing = value; }
    }
    /// <summary>
    /// 文本测量高度和文本测量宽度
    /// </summary>
    private Vector2 _sizeData;
    public Vector2 sizeData
    {
        get { return _sizeData; }
    }
    override protected void Awake()
    {
        base.Awake();
        rectTr = GetComponent<RectTransform>();
        comList = new List<RectTransform>();
        m_datas = new List<ItemDate>();
        startIndex = 0;
        _Spacing = 10;
        m_width = rectTr.sizeDelta.x;
        m_height = rectTr.sizeDelta.y;
        OnChangeViewWH += ChangeViewWH;
        OnUpdateItem += UpdataChild;
        onValueChanged.AddListener(OnChange);
    }
    override protected void Start()
    {
        base.Start();

    }
    // Update is called once per frame
    void Update()
    {
        if (m_width != rectTr.sizeDelta.x || m_height != rectTr.sizeDelta.y)
        {
            m_width = rectTr.sizeDelta.x;
            m_height = rectTr.sizeDelta.y;
            //调用宽高改变时触发
            OnChangeViewWH.DynamicInvoke();
        }
    }
    /// <summary>
    /// 设置list子控件皮肤
    /// </summary>
    public RectTransform setSkin
    {
        set
        {
            m_Skin = value;
        }
    }
    /// <summary>
    /// 设置数据源
    /// </summary>
    /// <param name="datas"></param>
    public void setData<T>(List<T> datas)
    {
        if (m_Skin == null)
        {
            Debug.LogError("Item皮肤没有设置");
        }
        //初始化数据
        this.m_datas.Clear();
        this.content.localPosition = new Vector3(this.content.localPosition.x, 0, 0);
        startIndex = 0;
        int len = datas.Count;
        //计算容器宽度+距离底部的距离
        _sizeData = new Vector2(m_Skin.sizeDelta.x, (m_Skin.sizeDelta.y * len + _Spacing * len - _Spacing) + _m_down + _m_top);
        //更新数据
        for (int i = 0; i < datas.Count; i++)
        {
            ItemDate data = new ItemDate();
            data.index = i;
            data.data = datas[i];
            data.m_parent = this;
            this.m_datas.Add(data);
        }
        //宽高发生改变
        ChangeViewWH();
    }
    /// <summary>
    /// 储存ui成员变量
    /// </summary>
    /// <param name="skin"></param>
    /// <returns></returns>
    private Dictionary<string, Transform> setUIChildName(Transform skin)
    {
        Dictionary<string, Transform> map = new Dictionary<string, Transform>();
        for (int i = 0; i < skin.childCount; i++)
        {
            Transform childobj = skin.GetChild(i);
            map[childobj.gameObject.name] = childobj;
            if (childobj.childCount > 0)
            {
                setUIChildName(childobj);
            }
        }
        return map;
    }
    /// <summary>
    /// 宽高发生改变时
    /// </summary>
    public void ChangeViewWH() {
        //向上取整得到数量
        int colunm = Mathf.CeilToInt((m_height + _Spacing) / (m_Skin.sizeDelta.y + _Spacing));
        endIndex = startIndex + colunm;
        //超出的删除
        if (comList.Count > colunm + 1 && colunm > 0) 
        {
            for (int s = colunm + 1; s < comList.Count; s++) 
            {
                Destroy(comList[s].gameObject);
                comList.RemoveAt(s);
            }
            return;
        }
        //生成
        if (colunm <= m_datas.Count)
        {
            for (int i = comList.Count; i <= colunm; i++)
            {
                //加个保护
                if (i >= this.m_datas.Count) break;

                RectTransform skin = Instantiate(m_Skin);
                //添加item
                skin.gameObject.AddComponent<Item>();
                comList.Add(skin);
                //设置父节点
                skin.SetParent(this.content, false);
            }
        }
        //更新所有item
        updateItem(); 
        //赋值文本测量高度
        this.content.sizeDelta = new Vector2(this.content.sizeDelta.x, _sizeData.y);
    }
    /// <summary>
    /// 数据初始化
    /// </summary>
    private void updateItem()
    {
        for (int i = 0; i < comList.Count; i++)
        {
            //计算item的坐标,以距离左边和距离上边为基准
            comList[i].localPosition = new Vector3(_m_left, -(_m_top + i * (m_Skin.sizeDelta.y + _Spacing)), 0);
            updataView(comList[i], i);
        }
    }
    /// <summary>
    /// 刷新数据--根据item刷新
    /// </summary>
    /// <param name="_comList"></param>
    /// <param name="i"></param>
    private void updataView(RectTransform _comList, int i)
    {
        Item data = _comList.gameObject.GetComponent<Item>();
        data.index = this.m_datas[i].index;
        data.data = this.m_datas[i].data;
        data.m_parent = this.m_datas[i].m_parent;
        data.m_childName = setUIChildName(_comList);
        OnUpdateItem.DynamicInvoke(data);
    }
    /// <summary>
    /// 滑动改变时
    /// </summary>
    /// <param name="pos"></param>
    private void OnChange(Vector2 pos)
    {
        int colunm = endIndex - startIndex;
        int curindex = Mathf.FloorToInt(this.content.localPosition.y / (m_Skin.sizeDelta.y + _Spacing));
        if (curindex > startIndex && endIndex + 1 < this.m_datas.Count)  
        {
            //符合当前的item
            for (int i = 0; i < comList.Count; i++)
            {
                Item data = comList[i].gameObject.GetComponent<Item>();
                if (data.index == startIndex)
                {
                    comList[i].localPosition = new Vector3(_m_left, -_m_top - (endIndex + 1) * (m_Skin.sizeDelta.y + _Spacing), 0);
                    updataView(comList[i], endIndex + 1);
                    break;
                }
            }
            startIndex++;
            endIndex = startIndex + colunm;
        }
        else if (curindex < startIndex && startIndex - 1 >= 0 && pos.y < 1)    
        {
            for (int i = 0; i < comList.Count; i++)
            {
                Item data = comList[i].gameObject.GetComponent<Item>();
                if (data.index == endIndex)
                {
                    comList[i].localPosition = new Vector3(_m_left, -_m_top - (startIndex - 1) * (m_Skin.sizeDelta.y + _Spacing), 0);
                    //刷新单个数据
                    updataView(comList[i], startIndex - 1);
                    break;
                }
            }
            startIndex--;
            endIndex = startIndex + colunm;
        }
    }
    public virtual void UpdataChild(Item data) { }
}

transferir:

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

public class dataa {
    public string name;
    public int index;
}
public class addlist : MonoBehaviour
{
    public UIList list;
    public GameObject skin;
    public Button btn;
    // Start is called before the first frame update
    void Start()
    {
        list.setSkin = skin.GetComponent<RectTransform>();
        List<dataa> dic = new List<dataa>();
        for (int i = 0; i < 10; i++)
        {
            dataa date = new dataa();
            date.name = "名字" + i;
            date.index = i;
            dic.Add(date);
        }
        list.OnUpdateItem += change;
        list.m_left = 10;
        list.m_top = 20;
        list.m_down = 30;
        list.setData(dic);
        List<dataa> dica = new List<dataa>();
        btn.onClick.AddListener(delegate ()
        {
            dica.Clear();
            for (int i = 0; i < 10; i++)
            {
                dataa date = new dataa();
                date.name = "已经改变";
                date.index = i;
                dica.Add(date);
            }
            list.setData(dica);
        });
    }
    void change(Item item)
    {
        Text text = item.m_childName["Text"].GetComponent<Text>();
        Button on_btn = item.m_childName["Button"].GetComponent<Button>();
        text.text = "xx" + (item.data as dataa).index + "--" + (item.data as dataa).name;
        //按钮点击消息事件
        on_btn.onClick.AddListener(() =>
        {
            Debug.Log((item.data as dataa).index);
        });
    }
    void Update()
    {
        
    }
}

La estructura que todavía uso es la estructura Vista de desplazamiento. Eliminé el control deslizante y sincronicé el borde.
Insertar descripción de la imagen aquí
Simplemente lo agregué al componente y lo llamé. Siempre que los parámetros estén bien, está bien.
Insertar descripción de la imagen aquí
El punto de anclaje del elemento debe ser En la esquina superior izquierda:
Insertar descripción de la imagen aquí
Solo salsa ~, aprende los puntos de cada día, entonces la dirección del progreso debe ser correcta.

Supongo que te gusta

Origin blog.csdn.net/QO_GQ/article/details/119612555
Recomendado
Clasificación