Unity UGUI 无限循环scrollview 实现

看了ugui的scrollview的scrollview的实现方式,扩展它来实现无限循环模式应该是不现实了。于是就想着能用ngui的方式来改写吧。

原理:通过部分,整体的方式来实现拖拽

实现:整体部分运动

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;

/// 
/// 自定义scrollview 目前只支持水平拖拽
/// 
public class CommonScrollView : MonoBehaviour
{
    public enum EMoveDirection
    {
        none,
        left_to_right,
        right_to_left,
    }
    /// 
    /// 列表的父物体
    /// 
    [SerializeField]
    private Transform itemParent;
    /// 
    /// 容器
    /// 
    [SerializeField]
    private RectTransform container;
    /// 
    /// 列表之间的间隔
    /// 
    [SerializeField]
    private int itemGap;
    /// 
    /// 拖拽速度,聚焦速度
    /// 
    [SerializeField]
    private float dragSpeed,centerSpeed;
    /// 
    /// 列表
    /// 
    private List childs { get; set; }
    /// 
    /// 目标中心点
    /// 
    private Transform targetCenterTransform=null;
    /// 
    /// 列表的聚焦点 可能为空
    /// 
    public Transform CenterTransform { get { return GetCenterTransform(); } }
    private Transform lastCenterTransform;
    /// 
    /// 上一次聚焦的中心
    /// 
    public Transform LastCenterTransform { get { return lastCenterTransform; } }
    public delegate void CenterOnCalled(GameObject obj);
    private CenterOnCalled OnCenter;
    private EMoveDirection moveDirection { get; set; }

    private void Awake()
    {
        childs = itemParent.Childs();
    }

    // Update is called once per frame
    private void Update()
    {
        if (targetCenterTransform)
        {
            float x = targetCenterTransform.localPosition.x;
            if (x==0)//单击消除回调
            {
                targetCenterTransform = null;
                return;
            }
            if (x!=0)//移动
            {
                switch (moveDirection)
                {
                    case EMoveDirection.none:
                        {
                            int direction = -((int)Mathf.Sign(x));
                            MoveChildren(Mathf.Min(Mathf.Abs(x), centerSpeed) * direction);
                            break;
                        }
                    case EMoveDirection.left_to_right:
                        {
                            MoveChildren(Mathf.Min(Mathf.Abs(x), centerSpeed) * -1);
                            break;
                        }
                    case EMoveDirection.right_to_left:
                        {
                            MoveChildren(Mathf.Min(Mathf.Abs(x), centerSpeed) * 1);
                            break;
                        }
                }
            }

            if (targetCenterTransform.localPosition.x==0 && OnCenter != null)//聚焦回调
            {
                OnCenter(targetCenterTransform.gameObject);
                targetCenterTransform = null;
            }
        }
    }
    public void DoRegisterOnCenterCalled(CenterOnCalled oncenter)
    {
        this.OnCenter += oncenter;
    }

    public void UnRegisterOnCenterCalled(CenterOnCalled oncenter)
    {
        this.OnCenter -= oncenter;
    }
    /// 
    /// 自动聚焦
    /// 
    public void CenterOnAnimationly()
    {
        targetCenterTransform = GetCaluateTargetCenter();
    }
    /// 
    /// 带有动画的聚焦
    /// 
    public void CenterOnAnimationly(Transform transform)
    {
        targetCenterTransform = transform;
        moveDirection = EMoveDirection.none;
    }
    public void CenterOnAnimationly(int center_index,EMoveDirection moveDirection)
    {
        this.moveDirection = moveDirection;
        targetCenterTransform = childs[center_index];
    }
    /// 
    /// 直接聚焦
    /// 
    public void CenterOnDirectly(Transform transform)
    {
        if(transform.parent==itemParent)
        {
            MoveChildren(-transform.localPosition.x);
        }
        targetCenterTransform = transform;
    }
    /// 
    /// 拖拽
    /// 
    public void DragChildren(float delaPos)
    {
        moveDirection = EMoveDirection.none;
        delaPos *= dragSpeed;
        MoveChildren(delaPos);
    }
    /// 
    /// 重置位置 此方法特别重要,拖拽前需要调用一次该方法
    /// 
    public void ResetPosition(Transform startCenterTrans)
    {
        ResetPosition(childs.FindIndex(v => v == startCenterTrans));
    }
    public void ResetPosition(int index)
    {
        if (!itemParent)
        {
            itemParent = transform;
        }
        int centerChildIndex = index;// childs.FindIndex(v => v == startCenterTrans);// startCenterTrans.GetSiblingIndex();
        for (int i = 0; i < childs.Count; i++)
        {
            Transform child = itemParent.GetChild(i);
            Vector3 localPos = child.transform.localPosition;
            localPos.x = (i - centerChildIndex) * itemGap;
            child.localPosition = localPos;
        }
    }
    /// 
    /// 获取聚焦的自物体
    /// 
    private Transform GetCenterTransform()
    {
        return childs.Find(delegate (Transform item) { return item.localPosition.x == 0; });
    }
    /// 
    /// 移动所有子物体
    /// 
    private void MoveChildren(float delaPos)
    {
        if(childs.Count == 0)
        {
            return;
        }
        Transform leftTranform = childs[0];
        Transform rightTransform = childs[0];
        for (int i = 0; i < childs.Count; i++)
        {
            Transform child = childs[i];
            Vector3 localPos = child.localPosition;
            localPos.x += delaPos;
            child.localPosition = localPos;
            if (child.localPosition.x < leftTranform.localPosition.x)
            {
                leftTranform = child;
            }
            if (child.localPosition.x > rightTransform.localPosition.x)
            {
                rightTransform = child;
            }
        }
        if (delaPos > 0 && leftTranform.localPosition.x > (-container.sizeDelta.x / 2 - itemGap / 2))
        {
            Vector3 targetPos = leftTranform.localPosition;
            targetPos.x -= itemGap;
            rightTransform.localPosition = targetPos;
        }
        if (delaPos < 0 && rightTransform.localPosition.x < (container.sizeDelta.x / 2 + itemGap / 2))
        {
            Vector3 targetPos = rightTransform.localPosition;
            targetPos.x += itemGap;
            leftTranform.localPosition = targetPos;
        }
    }
    /// 
    /// 获得计算的聚焦点
    /// 
    private Transform GetCaluateTargetCenter()
    {
        if(childs.Count == 0)
        {
            return null;
        }
        Transform nearestCenterTrans = childs[0];
        for(int i=1;i
    /// 缓存上一次聚焦的物体
    /// 
    public void DoCachedLastCenterObj()
    {
        lastCenterTransform = GetCaluateTargetCenter();
    }
}

2:item 通过挂组件的方式,来实现拖拽

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;

/// 
/// 拖拽 配合CommonScrollView 使用
/// 
public class CommonDragScrollView : MonoBehaviour,IPointerDownHandler,IPointerUpHandler
{
    private CommonScrollView commonScrollView;
    public CommonScrollView SelfCommonScrollView { get { if (!commonScrollView) { commonScrollView = NGUITools.FindInParents(transform); } return commonScrollView;  } }
    private bool isDraging { get; set; }
    private bool startPos { get; set; }
    private Action onClickCalled;
    private Vector2 currentTouchPos { get; set; }
    private int screenWidth { get; set; }
    private Vector3 cachedLocalPos { get; set; }
    // Use this for initialization
    private void Start()
    {
        if(!commonScrollView)
        {
            commonScrollView = NGUITools.FindInParents(transform);
        }
        if(!commonScrollView)
        {
            Debug.LogError("The commonScrollView is null!");
        }
        screenWidth = (int)Screen.safeArea.width;
    }

    // Update is called once per frame
    private IEnumerator Draging()
    {
        while (isDraging)
        {
            yield return null;
            float delaX = 0;
            Vector2 nowMousePos = Input.mousePosition;
            delaX = (nowMousePos - currentTouchPos).x / screenWidth * 1024;
            currentTouchPos = Input.mousePosition;       
            commonScrollView.DragChildren(delaX);
        }
    }
    public void DoRegisterOnClickCalled(Action called)
    {
        onClickCalled += called;
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        commonScrollView.DoCachedLastCenterObj();
        cachedLocalPos = transform.localPosition;
        currentTouchPos = Input.mousePosition;
        isDraging = true;
        StartCoroutine(Draging());
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        isDraging = false;
        StopAllCoroutines();
        if (transform.localPosition == cachedLocalPos)//判断为单击
        {
            commonScrollView.CenterOnAnimationly(transform);
            onClickCalled?.Invoke(gameObject);
        }
        else
        {
            commonScrollView.CenterOnAnimationly();
        }
    }
}

本文只实现了水平无限循环的拖拽,如果想扩展的,可以在此基础上扩展。

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