【UGUI】无限列表 ScrollView List

本文介绍了如何利用UGUI的ScrollView组件实现无限滚动列表,关键在于调整Content大小及子节点位置。通过设置元点和锚点简化计算,并提供多列、单列布局的切换。此外,还创建了编辑器类提升Inspector界面的美观性,并针对Mask尺寸问题设计了“显示窗口”和ViewPort解决方案。最后,文章分享了在实际应用中遇到的问题和相应的优化措施。
摘要由CSDN通过智能技术生成

无线列表的实现好处有一些,主要方法是在ScrollView的值改变的时候改变已存在的渲染子节点位置。最核心的是计算Content的大小及里面的节点位置设置。

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

namespace Assets.UI
{
    /// <summary>
    /// Introduction: ScrollList
    /// Author: 	Cheng
    /// Time: 
    /// </summary>
    [DisallowMultipleComponent]
    [RequireComponent(typeof (ScrollRect))]
    public class ScrollList : MonoBehaviour
    {
        public delegate void OnItemRender(int index, Transform child);

        public OnItemRender onItemRender;

        /// <summary>
        /// 排序方式
        /// </summary>
        public enum Arrangement
        {
            /// <summary>
            /// 横排
            /// </summary>
            Horizontal = 0,

            /// <summary>
            /// 竖排
            /// </summary>
            Vertical,
        }

        /// <summary>
        /// 水平对齐
        /// </summary>
        public enum HorizontalAlign
        {
            /// <summary>
            /// 居左
            /// </summary>
            Left,

            /// <summary>
            /// 居中
            /// </summary>
            Middle,

            /// <summary>
            /// 局右
            /// </summary>
            Right,
        }

        /// <summary>
        /// 垂直对齐
        /// </summary>
        public enum VerticalAlign
        {
            /// <summary>
            /// 居上
            /// </summary>
            Top,

            /// <summary>
            /// 居中
            /// </summary>
            Middle,

            /// <summary>
            /// 局下
            /// </summary>
            Bottom,
        }


        public Arrangement arrangement = Arrangement.Vertical;

        /// <summary>
        /// 当选择水平或垂直流动是有用,指每行/列最大个数
        /// </summary>
        public int MaxPerLine
        {
            get { return maxPerLine; }
            set { SetMaxPerLine(value); }
        }

        /// <summary>
        /// 行距
        /// </summary>
        public float rowSpace = 0;

        /// <summary>
        /// 列距
        /// </summary>
        public float columuSpace = 0;

        public HorizontalAlign horizontalAlign = HorizontalAlign.Left;
        public VerticalAlign verticalAlign = VerticalAlign.Top;

        /// <summary>
        /// 边缘留空 上
        /// </summary>
        public float marginTop = 0;

        /// <summary>
        /// 边缘留空 下
        /// </summary>
        public float marginBottom = 0;

        /// <summary>
        /// 边缘留空 左
        /// </summary>
        public float marginLeft = 0;

        /// <summary>
        /// 边缘留空 右
        /// </summary>
        public float marginRight = 0;

        /// <summary>
        /// 渲染子节点
        /// </summary>
        public GameObject Item
        {
            get { return item; }
            set { SetItem(value); }
        }

        /// <summary>
        /// 总个数
        /// </summary>
        public int ChildCount
        {
            get { return childCount; }
            set { SetChildCount(value, true); }
        }

        /// <summary>
        /// 设置显示窗口大小
        /// </summary>
        public Vector2 ViewPort
        {
            get { return viewPort; }
            set { SetViewPort(value); }
        }

        GameObject item;
        ScrollRect scrollRect;
        // RectTransform mask;
        Vector2 viewPort;
        RectTransform content;
        Vector2 itemSize;
        List<Transform> items;
        Dictionary<int, int> contains;
        List<int> outOfContains;
        int childCount; //需要渲染的总数据个数
        int scrollLineIndex; //当前第一个元素索引
        int totalCount; //在UI中显示的个数(不乘以maxPerLine)
        Vector2 startPos; //第一个元素所在位置
        int startIndex; //当前渲染起始坐标
        int endIndex; //当前渲染结束坐标
        int maxPerLine;

        void Start()
        {
            maxPerLine = maxPerLine == 0 ? 1 : maxPerLine;
            items = new List<Transform>();
            contains = new Dictionary<int, int>();
            outOfContains = new List<int>();
            scrollRect = transform.GetComponent<ScrollRect>();
            // mask = scrollRect.GetComponentInChildren<Mask>().rectTransform;
            content = scrollRect.content;
            if (content == null)
            {
                Debug.Log("ScrollRect " + scrollRect.gameObject.name + " Has No Content, Please Check And Retry.");
                return;
            }
            content.anchorMax = new Vector2(0, 1);
            content.anchorMin = new Vector2(0, 1);
            content.pivot = new Vector2(0, 1);
            ReBuild();
        }


        /// <summary>
        /// 当子节点、Mask、maxPerLine
        /// </summary>
        public void ReBuild()
        {
            if (scrollRect == null || content == null || item == null) return;
            ResetChildren();

            Vector2 maskSize = viewPort;
            int count = 0;

            if (arrangement == Arrangement.Horizontal)
            {
                count = Mathf.CeilToInt(maskSize.x/itemSize.x) + 1; //横向列数
                startPos = Vector2.zero;
                startPos.x = marginLeft;
                if (verticalAlign == VerticalAlign.Top)
                {
                    startPos.y = -marginTop;
                }
                else if (verticalAlign == VerticalAlign.Middle)
                {
                    startPos.y = -(maskSize.y*0.5f - (itemSize.y*maxPerLine + (maxPerLine - 1)*rowSpace)*0.5f);
                }
                else if (verticalAlign == VerticalAlign.Bottom)
                {
                    startPos.y = -(maskSize.y - marginBottom - itemSize.y*maxPerLine - rowSpace*(maxPerLine - 1));
                }

                for (int i = 0; i < count; i++)
                {
                    for (int j = 0; j < maxPerLine; j++)
                    {
                        RectTransform child = CreateItem(i*maxPerLine + j);
                        child.localPosition = startPos +
                                              new Vector2(i*itemSize.x + i*columuSpace, -j*itemSize.y - j*rowSpace);
                    }
                }
            }
            else if (arrangement == Arrangement.Vertical)
            {
                count = Mathf.CeilToInt(maskSize.y/itemSize.y) + 1; //竖向行数
                startPos = Vector2.zero;
                startPos.y = -marginTop; //重置开始节点位置
                if (horizontalAlign == HorizontalAlign.Left)
                {
                    startPos.x = marginLeft;
                }
                else if (horizontalAlign == HorizontalAlign.Middle)
                {
                    startPos.x = (maskSize.x*0.5f - (itemSize.x*maxPerLine + (maxPerLine - 1)*columuSpace)*0.5f);
                }
                else if (horizontalAlign == HorizontalAlign.Right)
                {
                    startPos.x = maskSize.x - marginRight - itemSize.x*maxPerLine - columuSpace*(maxPerLine - 1);
                }

                for (int i = 0; i < count; i++)
                {
                    for (int j = 0; j < maxPerLine; j++)
                    {
                        RectTransform child = CreateItem(i*maxPerLine + j);
                        child.localPosition = startPos +
                                              new Vector2(j*itemSize.x + j*columuSpace, -i*itemSize.y - i*rowSpace);
                    }
                }
            }
            totalCount = count;

            SetChildCount(childCount, true);
            BackTop();

            scrollRect.onValueChanged.RemoveAllListeners();
            scrollRect.onValueChanged.AddListener(OnValueChanged);
        }

        /// <summary>
        /// 列表滚动
        /// </summary>
        /// <param name="vec"></param>
        private void OnValueChanged(Vector2 vec)
        {
            switch (arrangement)
            {
                case Arrangement.Horizontal:
                 //   if (vec.x < 0.0f || vec.x >=
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值