在這裏作爲筆記使用 直接上代碼(有事件在總結)
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 關於滑動的控制 參數
/// <summary>
/// 是否開始滑動 控制是否自動滑動
/// </summary>
bool isSlide = false;
bool isInitEnd = false;
bool isDragging = false;
/// <summary>
/// 滑動的起始座標
/// </summary>
float targethorizontal = 0;
/// <summary>
/// 滑動速度
/// </summary>
//float smooting = 0.3f;
/// <summary>
/// 間隔多少時間執行一次(單位秒)
/// </summary>
float timeInterval = 3f;
/// <summary>
/// 執行多少時間(單位秒)
/// </summary>
//float executionTime = 2f;
/// <summary>
/// 當前時間
/// </summary>
float currentTime = 0;
/// <summary>
/// 當前執行時間
/// </summary>
//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();
}
/// <summary>
/// 拖拽完位置的更新
/// </summary>
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);
}
}
/// <summary>
/// 初始化數據及開始更新
/// </summary>
/// <param name="height">元素的高度</param>
/// <param name="spacing">元素之間的間距</param>
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;
/// <summary>
/// 元素間相隔的距離
/// </summary>
private int distanceBetweenEles;
/// <summary>
/// 各元素到中心點的距離
/// </summary>
private float[] distanceToCenter;
/// <summary>
/// 最小距離的元素索引
/// </summary>
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);
}
/// <summary>
/// 劇中吸附更新
/// </summary>
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]); });
}
}
/// <summary>
/// Update 點擊之後進行更新移動
/// </summary>
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);
}
}
/// <summary>
/// 點擊移動事件
/// </summary>
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();
}
}