无线列表的实现好处有一些,主要方法是在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 >=