在这里作为笔记使用 直接上代码(有事件在总结)
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using DG.Tweening;
using System.Collections.Generic;
using UnityEngine.Analytics;
[RequireComponent(typeof(GridLayoutGroup))]
[RequireComponent(typeof(ContentSizeFitter))]
public class SuperScrollView : MonoBehaviour
{
[SerializeField]
int minAmount = 0;//实现无限滚动,需要的最少的child数量。屏幕上能看到的+一行看不到的,比如我在屏幕上能看到 2 行,每一行 2 个。则这个值为 2行*2个 + 1 行* 2个 = 6个。
RectTransform rectTransform;
GridLayoutGroup gridLayoutGroup;
ContentSizeFitter contentSizeFitter;
ScrollRect scrollRect;
RectTransform lockPos;
List<RectTransform> children = new List<RectTransform>();
Vector2 startPosition;
int amount = 0;
float itemHeight = 0;
float itemSpacing = 2;
public delegate void UpdateChildrenCallbackDelegate(int index, Transform trans);
public UpdateChildrenCallbackDelegate updateChildrenCallback = null;
int realIndex = -1;
int realIndexUp = -1; //从下往上;
bool hasInit = false;
Vector2 gridLayoutSize;
Vector2 gridLayoutPos;
Dictionary<Transform, Vector2> childsAnchoredPosition = new Dictionary<Transform, Vector2>();
Dictionary<Transform, int> childsSiblingIndex = new Dictionary<Transform, int>();
#region 关于滑动的控制 参数
///
/// 是否开始滑动 控制是否自动滑动
///
bool isSlide = false;
bool isInitEnd = false;
bool isDragging = false;
///
/// 滑动的起始坐标
///
float targethorizontal = 0;
///
/// 滑动速度
///
//float smooting = 0.3f;
///
/// 间隔多少时间执行一次(单位秒)
///
float timeInterval = 3f;
///
/// 执行多少时间(单位秒)
///
//float executionTime = 2f;
///
/// 当前时间
///
float currentTime = 0;
///
/// 当前执行时间
///
//float currentExecutionTime = 0;
#endregion
// Use this for initialization
void Start()
{
//StartCoroutine(InitChildren());
}
IEnumerator InitChildren()
{
yield return 0;
if (!hasInit)
{
//获取Grid的宽度;
rectTransform = GetComponent<RectTransform>();
gridLayoutGroup = GetComponent<GridLayoutGroup>();
gridLayoutGroup.enabled = false;
contentSizeFitter = GetComponent<ContentSizeFitter>();
contentSizeFitter.enabled = false;
gridLayoutPos = rectTransform.anchoredPosition;
gridLayoutSize = rectTransform.sizeDelta;
//注册ScrollRect滚动回调;
scrollRect = transform.parent.parent.GetComponent<ScrollRect>();
scrollRect.onValueChanged.AddListener((data) => { ScrollCallback(data); });
//得到锁定的位置
lockPos = transform.parent.Find("CenterPoint").GetComponent<RectTransform>();
//获取所有child anchoredPosition 以及 SiblingIndex;
for (int index = 0; index < transform.childCount; index++)
{
Transform child = transform.GetChild(index);
RectTransform childRectTrans = child.GetComponent<RectTransform>();
childsAnchoredPosition.Add(child, childRectTrans.anchoredPosition);
childsSiblingIndex.Add(child, child.GetSiblingIndex());
}
}
else
{
rectTransform.anchoredPosition = gridLayoutPos;
rectTransform.sizeDelta = gridLayoutSize;
children.Clear();
realIndex = -1;
realIndexUp = -1;
//children重新设置上下顺序;
foreach (var info in childsSiblingIndex)
{
info.Key.SetSiblingIndex(info.Value);
}
//children重新设置anchoredPosition;
for (int index = 0; index < transform.childCount; index++)
{
Transform child = transform.GetChild(index);
RectTransform childRectTrans = child.GetComponent<RectTransform>();
if (childsAnchoredPosition.ContainsKey(child))
{
childRectTrans.anchoredPosition = childsAnchoredPosition[child];
}
else
{
Debug.LogError("childsAnchoredPosition no contain " + child.name);
}
}
}
//获取所有child;
for (int index = 0; index < transform.childCount; index++)
{
Transform trans = transform.GetChild(index);
trans.gameObject.SetActive(true);
children.Add(transform.GetChild(index).GetComponent<RectTransform>());
//初始化前面几个;
UpdateChildrenCallback(children.Count - 1, transform.GetChild(index));
}
startPosition = rectTransform.anchoredPosition;
realIndex = children.Count - 1;
hasInit = true;
//如果需要显示的个数小于设定的个数;
for (int index = 0; index < minAmount; index++)
{
children[index].gameObject.SetActive(index < amount);
}
if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount)
{
//如果小了一行,则需要把GridLayout的高度减去一行的高度;
int row = (minAmount - amount) / gridLayoutGroup.constraintCount;
if (row > 0)
{
rectTransform.sizeDelta -= new Vector2(0, (gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y) * row);
}
}
else
{
//如果小了一列,则需要把GridLayout的宽度减去一列的宽度;
int column = (minAmount - amount) / gridLayoutGroup.constraintCount;
if (column > 0)
{
rectTransform.sizeDelta -= new Vector2((gridLayoutGroup.cellSize.x + gridLayoutGroup.spacing.x) * column, 0);
}
}
pos = new Vector2(rectTransform.anchoredPosition.x, rectTransform.anchoredPosition.y + 160f);
isInitEnd = true;
isSlide = true;
Init();
OnClickItemInit();
}
Vector2 pos;
// Update is called once per frame
void Update()
{
if (isInitEnd)
{
if (isSlide && !isStartMove && !isDragging && !isMove)
{
currentTime += Time.deltaTime;
if (timeInterval + Mathf.Epsilon <= currentTime)
{
//Debug.Log("currentTime的值:" + currentTime);
//currentExecutionTime += Time.deltaTime;
if (pos.y - m_Content.anchoredPosition.y < 0.6f/*executionTime + Mathf.Epsilon <= currentExecutionTime*/)
{
pos = new Vector2(m_Content.anchoredPosition.x, m_Content.anchoredPosition.y + itemHeight /*160*/);
currentTime = 0;
//currentExecutionTime = 0;
}
//Debug.Log("currentExecutionTime的值:" + currentExecutionTime);
//Debug.Log("Y滑动scrollRect.verticalNormalizedPosition:" + scrollRect.verticalNormalizedPosition);
//scrollRect.verticalNormalizedPosition = Mathf.Lerp(scrollRect.verticalNormalizedPosition, targethorizontal, Time.deltaTime * smooting);
m_Content.anchoredPosition = Vector2.Lerp(m_Content.anchoredPosition, pos, Time.deltaTime * speedSlide/*smooting*/);
}
}
ItemOnClickMoveUpdate();
centerUpdate();
}
}
void ScrollCallback(Vector2 data)
{
UpdateChildren();
}
///
/// 拖拽完位置的更新
///
void UpdateChildren()
{
if (transform.childCount < minAmount)
{
return;
}
Vector2 currentPos = rectTransform.anchoredPosition;//当前挂在UI物体的锚点
if (gridLayoutGroup.constraint == GridLayoutGroup.Constraint.FixedColumnCount)
{
float offsetY = currentPos.y - startPosition.y;
if (offsetY > 0)
{
//向上拉,向下扩展;
{
if (realIndex >= amount - 1)
{
startPosition = currentPos;
return;
}
float scrollRectUp = lockPos.transform.TransformPoint(Vector3.zero).y;//scrollRect.transform.TransformPoint(Vector3.zero).y;
Vector3 childBottomLeft = new Vector3(children[0].anchoredPosition.x, children[0].anchoredPosition.y - gridLayoutGroup.cellSize.y, 0f);//第一个子物体当前的世界坐标的位置
float childBottom = transform.TransformPoint(childBottomLeft).y;
if (childBottom >= scrollRectUp)
{
//Debug.Log("childBottom >= scrollRectUp");
//移动到底部;
for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
{
children[index].SetAsLastSibling();//把当前的UI放到底部
children[index].anchoredPosition = new Vector2(children[index].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y - gridLayoutGroup.cellSize.y - gridLayoutGroup.spacing.y);
realIndex++;
if (realIndex > amount - 1)
{
children[index].gameObject.SetActive(false);
}
else
{
UpdateChildrenCallback(realIndex, children[index]);
}
}
//GridLayoutGroup 底部加长;
rectTransform.sizeDelta += new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);
//更新child;
for (int index = 0; index < children.Count; index++)
{
children[index] = transform.GetChild(index).GetComponent<RectTransform>();
}
}
}
}
else
{
//Debug.Log("Drag Down");
//向下拉,下面收缩;
if (realIndex + 1 <= children.Count)
{
startPosition = currentPos;
return;
}
RectTransform scrollRectTransform = scrollRect.GetComponent<RectTransform>();
Vector3 scrollRectAnchorBottom = new Vector3(0, -scrollRectTransform.rect.height - gridLayoutGroup.spacing.y, 0f);
float scrollRectBottom = scrollRect.transform.TransformPoint(scrollRectAnchorBottom).y;
Vector3 childUpLeft = new Vector3(children[children.Count - 1].anchoredPosition.x, children[children.Count - 1].anchoredPosition.y, 0f);
float childUp = transform.TransformPoint(childUpLeft).y;
if (childUp < scrollRectBottom)
{
//Debug.Log("childUp < scrollRectBottom");
//把底部的一行 移动到顶部
for (int index = 0; index < gridLayoutGroup.constraintCount; index++)
{
children[children.Count - 1 - index].SetAsFirstSibling();
children[children.Count - 1 - index].anchoredPosition = new Vector2(children[children.Count - 1 - index].anchoredPosition.x, children[0].anchoredPosition.y + gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);
children[children.Count - 1 - index].gameObject.SetActive(true);
UpdateChildrenCallback(realIndex - children.Count - index, children[children.Count - 1 - index]);
}
realIndex -= gridLayoutGroup.constraintCount;
//GridLayoutGroup 底部缩短;
rectTransform.sizeDelta -= new Vector2(0, gridLayoutGroup.cellSize.y + gridLayoutGroup.spacing.y);
//更新child;
for (int index = 0; index < children.Count; index++)
{
children[index] = transform.GetChild(index).GetComponent<RectTransform>();
}
}
}
}
startPosition = currentPos;
//targethorizontal = currentPos.y;
}
void UpdateChildrenCallback(int index, Transform trans)
{
if (updateChildrenCallback != null)
{
updateChildrenCallback(index, trans);
}
}
///
/// 初始化数据及开始更新
///
/// 元素的高度
/// 元素之间的间距
public void SetAmount(float height,float spacing = 2f)
{
amount = int.MaxValue;
itemHeight = height;
itemSpacing = spacing;
StartCoroutine(InitChildren());
}
public void DragStart()
{
isSlide = false;
isDragging = true;
isMove = true;
Debug.Log("是否进行轮播:" + isSlide);
}
public void DragEnd()
{
isSlide = true;
isDragging = false;
newPos = new Vector2(m_Content.anchoredPosition.x, children[minEleNum].anchoredPosition.y-m_CenterPoint.anchoredPosition.y - 2);
Debug.Log("是否进行轮播:" + isSlide);
}
#region 拖拽自动吸附居中
private RectTransform centerPoint;
private Vector2 newPos;
///
/// 元素间相隔的距离
///
private int distanceBetweenEles;
///
/// 各元素到中心点的距离
///
private float[] distanceToCenter;
///
/// 最小距离的元素索引
///
private int minEleNum;
private bool isMove = false;
private void Init()
{
int elelength = children.Count;
centerPoint = lockPos;
distanceToCenter = new float[elelength];
//获取元素间隔距离
distanceBetweenEles = (int)Mathf.Abs(children[1].anchoredPosition.y - children[0].anchoredPosition.y);
}
///
/// 剧中吸附更新
///
private void centerUpdate()
{
//if (isSlide) return;
if (isDragging)
{
if (Vector2.Distance(rectTransform.anchoredPosition, newPos) > 5f)
{
for (int i = 0; i < children.Count; i++)
{
//得到每个元素到中心点的距离
distanceToCenter[i] = Mathf.Abs(centerPoint.transform.position.y - children[i].transform.position.y);
Debug.Log("第"+ i + "个元素距中心点的位置:" + distanceToCenter[i]);
}
//获得最近距离
float minDist = Mathf.Min(distanceToCenter);
for (int i = 0; i < children.Count; i++)
{
//找到最小距离的元素索引
if (minDist == distanceToCenter[i])
{
minEleNum = i;
break;
}
}
}
}
if (!isDragging)
{
if (isMove)
{
//当前没有拖拽,自动吸附居中
if (newPos.y - m_Content.anchoredPosition.y < 0.6f)
{
isMove = false;
}
rectTransform.anchoredPosition = Vector2.Lerp(m_Content.anchoredPosition, newPos, Time.deltaTime * 5f);
}
//newPos = new Vector2(m_Content.anchoredPosition.x, minEleNum * -distanceBetweenEles);
}
}
#endregion
#region 点击进行移动
List<RectTransform> currentRectTList = new List<RectTransform>();
List<Button> currentButtonList = new List<Button>();
ScrollRect currentScrollRect;
RectTransform m_Content;
RectTransform m_CenterPoint;
bool isStartMove = false;
float offsetY = 0;
Vector2 tagetPos;
float speedSlide = 2f;
void OnClickItemInit()
{
currentScrollRect = scrollRect;
m_Content = currentScrollRect.content;
m_CenterPoint = lockPos;
foreach (RectTransform itemButton in m_Content)
{
currentRectTList.Add(itemButton);
}
Debug.Log("Content下子物体的个数:" + currentRectTList.Count);
for (int i = 0; i < currentRectTList.Count; i++)
{
int index = i;
currentButtonList.Add(currentRectTList[i].GetComponent<Button>());
currentButtonList[i].onClick.AddListener(delegate { OnClickMoveContent(currentRectTList[index]); });
}
}
///
/// Update 点击之后进行更新移动
///
void ItemOnClickMoveUpdate()
{
if (isStartMove && !isMove)
{
if (offsetY - m_Content.anchoredPosition.y < 0.6f /*Mathf.Epsilon + m_Content.anchoredPosition.y == offsetY*/)
{
isStartMove = false;
Debug.Log("结束时ContentY轴的位置为:" + m_Content.anchoredPosition.y);
for (int i = 0; i < currentButtonList.Count; i++)
{
int index = i;
currentButtonList[i].onClick.AddListener(delegate { OnClickMoveContent(currentRectTList[index]); });
}
}
m_Content.anchoredPosition = Vector2.Lerp(m_Content.anchoredPosition, tagetPos, Time.deltaTime * speedSlide);
Debug.Log("结束时ContentY轴的位置为:" + m_Content.anchoredPosition.y);
Debug.Log("结束时offsetY:" + offsetY);
}
}
///
/// 点击移动事件
///
void OnClickMoveContent(RectTransform currentR)
{
Debug.Log("currentR的值为:" + currentR.anchoredPosition);
//float temp = m_Content.anchoredPosition.y;
offsetY = Mathf.Abs(currentR.anchoredPosition.y - m_CenterPoint.anchoredPosition.y - itemSpacing);
//offsetY = temp + offsetY;
tagetPos = new Vector2(m_Content.anchoredPosition.x, offsetY);
Debug.Log("移动的偏移值为:" + offsetY);
isStartMove = true;
}
#endregion
}
这是相关的类可直接使用 使用方法为:
挂在到ScrollView 下面的Content组件上面
然后获取到该组件上面的这个代码 调用 superScrollView.SetAmount(160f,2f);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TestSuperScrollView : MonoBehaviour
{
SuperScrollView superScrollView;
int amount = 500;
private void Start()
{
//初始化数据列表
superScrollView = transform.Find("Viewport/Content").GetComponent<SuperScrollView>();
superScrollView.SetAmount(160f,2f);
//superScrollView.updateChildrenCallback = UpdateChildrenCallback;
}
void UpdateChildrenCallback(int index, Transform trans)
{
Debug.Log("UpdateChildrenCallback: index=" + index + " name:" + trans.name);
Text text = trans.Find("Text").GetComponent<Text>();
text.text = index.ToString();
}
}