无线列表的实现好处有一些,主要方法是在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 >= 1.0f)
                 //       return;
                        vec.x = Mathf.Clamp(vec.x, 0, 1);
                    break;
                case Arrangement.Vertical:
                 //   if (vec.y <= 0.0f || vec.y >= 1.0f)
                 //       return;
                        vec.y = Mathf.Clamp(vec.y, 0, 1);
                    break;
            }

            int curLineIndex = GetCurLineIndex();
            if (curLineIndex != scrollLineIndex)
                UpdateRectItem(curLineIndex, false);
        }

        /// <summary>
        /// 获取页面第一行索引
        /// </summary>
        /// <returns></returns>
        private int GetCurLineIndex()
        {
            switch (arrangement)
            {
                case Arrangement.Horizontal:
                    return
                        Mathf.FloorToInt(Mathf.Abs(Mathf.Abs(content.anchoredPosition.x) - marginLeft)/
                                         (columuSpace + itemSize.x));
                case Arrangement.Vertical:
                    return
                        Mathf.FloorToInt(Mathf.Abs(Mathf.Abs(content.anchoredPosition.y) - marginTop)/
                                         (rowSpace + itemSize.y));
            }
            return 0;
        }

        /// <summary>
        /// 更新数据(待修改问出现的才刷新)
        /// </summary>
        /// <param name="curLineIndex"></param>
        /// <param name="forceRender"></param>
        private void UpdateRectItem(int curLineIndex, bool forceRender)
        {
            if (curLineIndex < 0)
                return;
            startIndex = curLineIndex*maxPerLine;
            endIndex = (curLineIndex + totalCount)*maxPerLine;
            if (endIndex >= childCount)
                endIndex = childCount;

            contains.Clear(); //渲染序号
            outOfContains.Clear(); //items的索引
            for (int i = 0; i < items.Count; i++)//如果当前已渲染的item中包含
            {
                int index = int.Parse(items[i].gameObject.name);
                if (index < startIndex || index >= endIndex)
                {
                    outOfContains.Add(i);
                    items[i].gameObject.SetActive(false);
                }
                else
                {
                    items[i].gameObject.SetActive(true);
                    contains.Add(index, i);
                }
            }

            // *************更改渲染****************
            for (int i = startIndex; i < endIndex; i++)
            {
                if (!contains.ContainsKey(i))
                {
                    Transform child = items[outOfContains[0]];
                    outOfContains.RemoveAt(0);
                    child.gameObject.SetActive(true);
                    int row = i/maxPerLine;
                    int col = i%maxPerLine;
                    if (arrangement == Arrangement.Vertical)
                        child.localPosition = startPos +
                                              new Vector2(col*itemSize.x + (col)*columuSpace,
                                                  -row*itemSize.y - (row)*rowSpace);
                    else
                        child.localPosition = startPos +
                                              new Vector2(row*itemSize.x + (row)*columuSpace,
                                                  -col*itemSize.y - (col)*rowSpace);
                    child.gameObject.name = i.ToString();
                    if (onItemRender != null)
                        onItemRender(i, child);
                }
                else if (forceRender)
                {
                    if (onItemRender != null)
                        onItemRender(i, items[contains[i]]);
                }
            }

            scrollLineIndex = curLineIndex;
        }

        /// <summary>
        /// 移除当前所有
        /// </summary>
        private void ResetChildren()
        {
            items.Clear();
            for (int i = 0; i < content.childCount; i++)
            {
                Transform child = content.GetChild(i);
                child.gameObject.SetActive(false);
            }
        }

        /// <summary>
        /// 创建新节点
        /// </summary>
        /// <param name="index"></param>
        private RectTransform CreateItem(int index)
        {
            Transform child;
            if (content.childCount > index)
            {
                child = content.GetChild(index);
            }
            else
            {
                GameObject obj = GameObject.Instantiate(item) as GameObject;
                obj.transform.SetParent(content);
                obj.transform.localScale = Vector3.one;
                child = obj.transform;
            }
            child.gameObject.name = index.ToString();
            items.Add(child);

            return child as RectTransform;
        }

        /// <summary>
        /// 设置资源
        /// </summary>
        /// <param name="child"></param>
        public void SetItem(GameObject child)
        {
            if (child == null) return;
            this.item = child;
            RectTransform itemTrans = child.transform as RectTransform;
            itemTrans.pivot = new Vector2(0, 1);
            itemSize = itemTrans.sizeDelta;
            ReBuild();
        }

        /// <summary>
        /// 更新需要渲染的个数
        /// </summary>
        /// <param name="value"></param>
        public void SetChildCount(int value, bool forceRender)
        {
            if (value < 0) childCount = 0;
            else childCount = value;

            if (content == null) return;

            int rc = Mathf.CeilToInt((float) childCount/(float) maxPerLine); //设置content的大小
            if (arrangement == Arrangement.Horizontal)
            {
                content.sizeDelta = new Vector2(marginLeft + marginRight + itemSize.x*rc + columuSpace*(rc - 1),
                    viewPort.y);
                if (content.sizeDelta.x > viewPort.x && content.anchoredPosition.x < viewPort.x - content.sizeDelta.x)
                    content.anchoredPosition = new Vector2(viewPort.x - content.sizeDelta.x, content.anchoredPosition.y);
            }
            else
            {
                content.sizeDelta = new Vector2(viewPort.x, marginTop + marginBottom + itemSize.y*rc + rowSpace*(rc - 1));
                if (content.sizeDelta.y > viewPort.y && content.anchoredPosition.y > content.sizeDelta.y - viewPort.y)
                    content.anchoredPosition = new Vector2(content.anchoredPosition.x, content.sizeDelta.y - viewPort.y);
            }
            UpdateRectItem(GetCurLineIndex(), true);
        }

        /// <summary>
        /// 添加子节点
        /// </summary>
        /// <param name="index"></param>
        public void AddChild(int index)
        {
            if (index < 0) return;
            startIndex = scrollLineIndex*maxPerLine;
            endIndex = (scrollLineIndex + totalCount)*maxPerLine;
            SetChildCount(childCount + 1, index >= startIndex && index < endIndex);
        }

        /// <summary>
        /// 删除子节点
        /// </summary>
        /// <param name="index"></param>
        public void RemoveChild(int index)
        {
            if (index < 0 || index >= childCount) return;
            startIndex = scrollLineIndex*maxPerLine;
            endIndex = (scrollLineIndex + totalCount)*maxPerLine;
            SetChildCount(childCount - 1, index >= startIndex && index < endIndex);
        }


        /// <summary>
        /// 设置显示窗口大小
        /// </summary>
        /// <param name="port"></param>
        public void SetViewPort(Vector2 port)
        {
            if (port == viewPort) return;
            viewPort = port;
            ReBuild();
        }

        /// <summary>
        /// 设置行列最大
        /// </summary>
        /// <param name="max"></param>
        public void SetMaxPerLine(int max)
        {
            maxPerLine = max;
            ReBuild();
        }

        /// <summary>
        /// 返回顶部
        /// </summary>
        public void BackTop()
        {
            content.localPosition = Vector3.zero;
            UpdateRectItem(0, true);
        }

        /// <summary>
        /// 返回底部
        /// </summary>
        public void BackBottom()
        {
            if (arrangement == Arrangement.Vertical)
            {
                content.localPosition = new Vector3(0, -viewPort.y + content.sizeDelta.y, 0);
            }
            else
            {
                content.localPosition = new Vector3(viewPort.x - content.sizeDelta.x, 0);
            }
            UpdateRectItem(Mathf.CeilToInt((float) childCount/(float) maxPerLine) - totalCount + 1, true);
        }

        public void RefreshViewItem()
        {
            UpdateRectItem(scrollLineIndex, true);
        }

    }
}


 

 

 

为了计算方便,我把他们的元点和锚点全设置为左上角,减少一大堆工作量。

实现效果如下

 

UnityScroll View unityscrollview无尽列表_UI

可以设置为多列,单列,动态切换。

为了Inspector的美观,写了一个编辑器类

 

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

/// <summary>
/// Introduction: ScrollListEditor
/// Author: 	Cheng
/// Time: 
/// </summary>
[CustomEditor(typeof(ScrollList))]
public class ScrollListEditor : Editor
{
    ScrollList script;

    void OnEnable()
    {
        script = (ScrollList) target;
    }

    public override void OnInspectorGUI()
    {
        GUILayout.Space(10);
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel("排列方式");
        script.arrangement = (ScrollList.Arrangement)EditorGUILayout.EnumPopup(script.arrangement);
        EditorGUILayout.EndHorizontal();
        GUILayout.Space(5);

        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel("对齐方式");
        EditorGUILayout.BeginVertical();
        script.verticalAlign = (ScrollList.VerticalAlign)EditorGUILayout.EnumPopup(script.verticalAlign);
        script.horizontalAlign = (ScrollList.HorizontalAlign)EditorGUILayout.EnumPopup(script.horizontalAlign);
        EditorGUILayout.EndVertical();
        EditorGUILayout.EndHorizontal();
        GUILayout.Space(5);

        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel("行距");
        script.rowSpace = EditorGUILayout.FloatField(script.rowSpace);
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel("列距");
        script.columuSpace = EditorGUILayout.FloatField(script.columuSpace);
        EditorGUILayout.EndHorizontal();

        GUILayout.Space(5);
        EditorGUILayout.BeginHorizontal();
        if(script.arrangement == ScrollList.Arrangement.Horizontal)
            EditorGUILayout.PrefixLabel("每列个数");
        else
            EditorGUILayout.PrefixLabel("每行个数");
        int num = EditorGUILayout.IntField(script.MaxPerLine);
        if (num > 0 && num != script.MaxPerLine)
            script.MaxPerLine = num;
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel("边缘距离");
        EditorGUILayout.BeginVertical();
        EditorGUILayout.BeginHorizontal();
        GUILayout.Label("上");
        script.marginTop = EditorGUILayout.FloatField(script.marginTop);
        GUILayout.Label("下");
        script.marginBottom = EditorGUILayout.FloatField(script.marginBottom);
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.BeginHorizontal();
        GUILayout.Label("左");
        script.marginLeft = EditorGUILayout.FloatField(script.marginLeft);
        GUILayout.Label("右");
        script.marginRight = EditorGUILayout.FloatField(script.marginRight);
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.EndVertical();
        EditorGUILayout.EndHorizontal();

        GUILayout.Space(10);

        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel("总个数");
        script.ChildCount = EditorGUILayout.IntField(script.ChildCount);
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel("显示窗口");
        script.ViewPort = EditorGUILayout.Vector2Field("",script.ViewPort);
        EditorGUILayout.EndHorizontal();

        GUILayout.Space(5);
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel("子节点");
        script.Item = (GameObject)EditorGUILayout.ObjectField(script.Item, typeof (GameObject), true);
        EditorGUILayout.EndHorizontal();

        if(GUI.changed)
            script.ReBuild();
    }
}


 

效果:

UnityScroll View unityscrollview无尽列表_List_02

当选择水平布局的时候“每行个数”会切换为“每列个数”。

由于Mask的锚点设置时相对父节点全局拉伸,我没看到怎么获取他的Size, 所以写了一个 “显示窗口” 及ViewPort来手动填他的大小。

 

 

实际使用中发现了一些问题,做出一些优化,最终代码为:

 

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

namespace Assets.UI
{
    /// <summary>
    /// Introduction: 无限列表
    ///             Content上禁止挂载ContentSizeFilter和LayOutGroup之类组件
    /// 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 Child
        {
            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;
        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>();
            content = scrollRect.content;
            if (content == null)
            {
                Debug.Log("ScrollRect " + scrollRect.gameObject.name + " Has No Content, Please Check And Retry.");
                return;
            }
            viewPort = scrollRect.viewport.rect.size;

            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 >= 1.0f)
                 //       return;
                        vec.x = Mathf.Clamp(vec.x, 0, 1);
                    break;
                case Arrangement.Vertical:
                 //   if (vec.y <= 0.0f || vec.y >= 1.0f)
                 //       return;
                        vec.y = Mathf.Clamp(vec.y, 0, 1);
                    break;
            }

            int curLineIndex = GetCurLineIndex();
            if (curLineIndex != scrollLineIndex)
                UpdateRectItem(curLineIndex, false);
        }

        /// <summary>
        /// 获取页面第一行索引
        /// </summary>
        /// <returns></returns>
        private int GetCurLineIndex()
        {
            switch (arrangement)
            {
                case Arrangement.Horizontal:
                    return
                        Mathf.FloorToInt(Mathf.Abs(content.anchoredPosition.x < 0.1f? content.anchoredPosition.x : 0.1f - marginLeft)/
                                         (columuSpace + itemSize.x));
                case Arrangement.Vertical:
                    return
                        Mathf.FloorToInt(Mathf.Abs(content.anchoredPosition.y>-0.1f?content.anchoredPosition.y:-0.1f - marginTop)/
                                         (rowSpace + itemSize.y));
            }
            return 0;
        }

        /// <summary>
        /// 更新数据(待修改问出现的才刷新)
        /// </summary>
        /// <param name="curLineIndex"></param>
        /// <param name="forceRender"></param>
        private void UpdateRectItem(int curLineIndex, bool forceRender)
        {
            if (curLineIndex < 0)
                return;
            startIndex = curLineIndex*maxPerLine;
            endIndex = (curLineIndex + totalCount)*maxPerLine;
            if (endIndex >= childCount)
                endIndex = childCount;

            contains.Clear(); //渲染序号
            outOfContains.Clear(); //items的索引
            for (int i = 0; i < items.Count; i++)//如果当前已渲染的item中包含
            {
                int index = int.Parse(items[i].gameObject.name);
                if (index < startIndex || index >= endIndex)
                {
                    outOfContains.Add(i);
                    items[i].gameObject.SetActive(false);
                }
                else
                {
                    items[i].gameObject.SetActive(true);
                    contains.Add(index, i);
                }
            }

            // *************更改渲染****************
            for (int i = startIndex; i < endIndex; i++)
            {
                if (!contains.ContainsKey(i))
                {
                    Transform child = items[outOfContains[0]];
                    outOfContains.RemoveAt(0);
                    child.gameObject.SetActive(true);
                    int row = i/maxPerLine;
                    int col = i%maxPerLine;
                    if (arrangement == Arrangement.Vertical)
                        child.localPosition = startPos +
                                              new Vector2(col*itemSize.x + (col)*columuSpace,
                                                  -row*itemSize.y - (row)*rowSpace);
                    else
                        child.localPosition = startPos +
                                              new Vector2(row*itemSize.x + (row)*columuSpace,
                                                  -col*itemSize.y - (col)*rowSpace);
                    child.gameObject.name = i.ToString();
                    if (onItemRender != null)
                        onItemRender(i, child);
                }
                else if (forceRender)
                {
                    if (onItemRender != null)
                        onItemRender(i, items[contains[i]]);
                }
            }

            scrollLineIndex = curLineIndex;
        }

        /// <summary>
        /// 移除当前所有
        /// </summary>
        private void ResetChildren()
        {
            items.Clear();
            for (int i = 0; i < content.childCount; i++)
            {
                Transform child = content.GetChild(i);
                child.gameObject.SetActive(false);
            }
        }

        /// <summary>
        /// 创建新节点
        /// </summary>
        /// <param name="index"></param>
        private RectTransform CreateItem(int index)
        {
            Transform child;
            if (content.childCount > index)
            {
                child = content.GetChild(index);
            }
            else
            {
                GameObject obj = GameObject.Instantiate(item) as GameObject;
                obj.transform.SetParent(content);
                obj.transform.localScale = Vector3.one;
                child = obj.transform;
            }
            child.gameObject.name = index.ToString();
            items.Add(child);

            return child as RectTransform;
        }

        /// <summary>
        /// 设置资源
        /// </summary>
        /// <param name="child"></param>
        public void SetItem(GameObject child)
        {
            if (child == null) return;
            this.item = child;
            RectTransform itemTrans = child.transform as RectTransform;
            itemTrans.pivot = new Vector2(0, 1);
            itemSize = itemTrans.sizeDelta;
            ReBuild();
        }

        /// <summary>
        /// 更新需要渲染的个数
        /// </summary>
        /// <param name="value"></param>
        public void SetChildCount(int value, bool forceRender)
        {
            if (value < 0) childCount = 0;
            else childCount = value;

            if(totalCount <= 0)//还未初始化
                return;
            if (value > items.Count && items.Count < maxPerLine * totalCount)
            {
                //当前格子数量少于应生成的数量
                int count = items.Count;
                int max = value < maxPerLine*totalCount ? value : maxPerLine*totalCount;
                for (int i = count; i < max; i++)
                {
                    int row = i / maxPerLine;
                    int col = i % maxPerLine;
                    RectTransform child = CreateItem(i);
                    if (arrangement == Arrangement.Vertical)
                        child.localPosition = startPos +
                                              new Vector2(col * itemSize.x + (col) * columuSpace,
                                                  -row * itemSize.y - (row) * rowSpace);
                    else
                        child.localPosition = startPos +
                                              new Vector2(row * itemSize.x + (row) * columuSpace,
                                                  -col * itemSize.y - (col) * rowSpace);
                }
            } 

            if (content == null) return;

            int rc = Mathf.CeilToInt((float) childCount/(float) maxPerLine); //设置content的大小
            if (arrangement == Arrangement.Horizontal)
            {
                content.sizeDelta = new Vector2(marginLeft + marginRight + itemSize.x*rc + columuSpace*(rc - 1),
                    viewPort.y);
                if (content.sizeDelta.x > viewPort.x && content.anchoredPosition.x < viewPort.x - content.sizeDelta.x)
                    content.anchoredPosition = new Vector2(viewPort.x - content.sizeDelta.x, content.anchoredPosition.y);
            }
            else
            {
                content.sizeDelta = new Vector2(viewPort.x, marginTop + marginBottom + itemSize.y*rc + rowSpace*(rc - 1));
                if (content.sizeDelta.y > viewPort.y && content.anchoredPosition.y > content.sizeDelta.y - viewPort.y)
                    content.anchoredPosition = new Vector2(content.anchoredPosition.x, content.sizeDelta.y - viewPort.y);
            }
            UpdateRectItem(GetCurLineIndex(), true);
        }

        /// <summary>
        /// 添加子节点
        /// </summary>
        /// <param name="index"></param>
        public void AddChild(int index)
        {
            if (index < 0) return;
            startIndex = scrollLineIndex*maxPerLine;
            endIndex = (scrollLineIndex + totalCount)*maxPerLine;
            SetChildCount(childCount + 1, index >= startIndex && index < endIndex);
        }

        /// <summary>
        /// 删除子节点
        /// </summary>
        /// <param name="index"></param>
        public void RemoveChild(int index)
        {
            if (index < 0 || index >= childCount) return;
            startIndex = scrollLineIndex*maxPerLine;
            endIndex = (scrollLineIndex + totalCount)*maxPerLine;
            SetChildCount(childCount - 1, index >= startIndex && index < endIndex);
        }


        /// <summary>
        /// 设置显示窗口大小(现在貌似可以废弃了)
        /// </summary>
        /// <param name="port"></param>
        public void SetViewPort(Vector2 port)
        {
            if (port == viewPort) return;
            viewPort = port;
            ReBuild();
        }

        /// <summary>
        /// 设置行列最大
        /// </summary>
        /// <param name="max"></param>
        public void SetMaxPerLine(int max)
        {
            maxPerLine = max;
            ReBuild();
        }

        /// <summary>
        /// 返回顶部
        /// </summary>
        public void BackTop()
        {
            content.localPosition = Vector3.zero;
            UpdateRectItem(0, true);
        }

        /// <summary>
        /// 返回底部
        /// </summary>
        public void BackBottom()
        {
            if (arrangement == Arrangement.Vertical)
            {
                content.localPosition = new Vector3(0, -viewPort.y + content.sizeDelta.y, 0);
            }
            else
            {
                content.localPosition = new Vector3(viewPort.x - content.sizeDelta.x, 0);
            }
            UpdateRectItem(Mathf.CeilToInt((float) childCount/(float) maxPerLine) - totalCount + 1, true);
        }

        public void RefreshViewItem()
        {
            UpdateRectItem(scrollLineIndex, true);
        }


        public void SetArrangement(int arr)
        {
            arrangement = (Arrangement) arr;
        }

        public void SetHorizontal(int h)
        {
            horizontalAlign = (HorizontalAlign) h;
        }

        public void SetVerticle(int v)
        {
            verticalAlign = (VerticalAlign) v;
        }
    }
}