關於UGUI ScrollView 的自動無限滾動,點擊滾動及拖拽滾動的結合

在這裏作爲筆記使用 直接上代碼(有事件在總結)

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();
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章