关于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 关于滑动的控制 参数
    /// 
    /// 是否开始滑动 控制是否自动滑动
    /// 
    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();
    }
}

你可能感兴趣的:(Unity,UGUI)