UGUI ScrollRect滑动居中CenterOnChild实现(修改)

原帖:http://www.cnblogs.com/suoluo/p/5535420.html

上述文章是昨天项目遇到这种需求无意间搜到的,不过这篇的缺陷是只能水平实现滑动居中,所以我原基础上更改了一下。

1. 仅适用于水平方向和垂直方向(=-= 复制修改)拖动的ScrollRect。
2. ScrollRect中的Grid必须使用GridLayoutGroup。(虽然是居中表现 ChildAligenment也【不要】选择成Center Middle)
3. 由于需要知道ScrollRect的宽度以便计算中心位置,故ScrollRect的Anchors的四个小三角中的上面或者下面的一对角不得分离,不然宽度计算出错,即需要:Anchors.Min.x == Anchors.Max.x。最好四角合一。(解释一下,因为不同的Anchors下 content的localPosition坐标不同,所以用到的公式不同,这个公式可以根据自己的Anchors需求自己列出
4. 由于是通过设置ScrollRect's content的localPosition实现,故需要将ScrollRect的中心点Pivot与content的中心点均置于自身最左边(0, 0.5)。(这条内容我没有尝试,因为时间紧张还没有看水平的东西,直接修改成垂直的了)
5. 由于第一个与最后一个子物体需要停留在中间,故ScrollRect的Movement Type需要设置为Unrestricted。该项会在运行时自动设置。(这条不需要说明了,不过表现垂直的时候要在content下添加ContentSizeFitter,并且把Vertical Fit 选择MinSize)


上面几条是我个人的理解,也不可能全对,欢迎指出。

下面附上代码


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

/// 
/// 
/// 拖动ScrollRect结束时始终让一个子物体位于中心位置。
/// 
/// 
public class CenterOnChild : MonoBehaviour, IEndDragHandler, IDragHandler
{
    public enum AxisType
    {
        Vertical,
        Horizontal
    }

    public AxisType m_AxisType = AxisType.Vertical;
    //将子物体拉到中心位置时的速度
    public float centerSpeed = 40f;

    //注册该事件获取当拖动结束时位于中心位置的子物体
    public delegate void OnCenterHandler(GameObject centerChild);
    public event OnCenterHandler onCenter;

    public ScrollRect _scrollView;
    public Transform _container;

    private List _childrenPos = new List();
    private float _targetPos;
    private bool _centering = false;

    void Awake()
    {
        if (_scrollView == null)
        {
            Debug.LogError("CenterOnChild: No ScrollRect");
            return;
        }

        GridLayoutGroup grid;
        grid = _container.GetComponent();
        if (grid == null)
        {
            Debug.LogError("CenterOnChild: No GridLayoutGroup on the ScrollRect's content");
            return;
        }

        _scrollView.movementType = ScrollRect.MovementType.Unrestricted;

        //计算第一个子物体位于中心时的位置
        float childPosY;
        float childPosX;

        switch (m_AxisType)
        {
            case AxisType.Vertical:
                childPosY = _container.localPosition.y - (_scrollView.GetComponent().rect.height * 0.5f - grid.cellSize.y * 0.5f);//垂直的公式
                _childrenPos.Add(childPosY);
                //缓存所有子物体位于中心时的位置
                for (int i = 0; i < _container.childCount - 1; i++)
                {
                    childPosY += grid.cellSize.y + grid.spacing.y;
                    _childrenPos.Add(childPosY);
                }
                break;
            case AxisType.Horizontal:
                childPosX = _scrollView.GetComponent().rect.width * 0.5f - grid.cellSize.x * 0.5f;//水平的公式
                //缓存所有子物体位于中心时的位置
                for (int i = 0; i < _container.childCount - 1; i++)
                {
                    childPosX += grid.cellSize.x + grid.spacing.x;
                    _childrenPos.Add(childPosX);
                }
                _childrenPos.Add(childPosX);
                break;
        }






    }

    void Update()
    {
        if (_centering)
        {
            Vector3 v = _container.localPosition;
            switch (m_AxisType)
            {
                case AxisType.Vertical:
                    v.y = Mathf.Lerp(_container.localPosition.y, _targetPos, centerSpeed * Time.deltaTime);
                    _container.localPosition = v;
                    if (Mathf.Abs(_container.localPosition.y - _targetPos) < 0.01f)
                    {
                        _centering = false;
                    }
                    break;
                case AxisType.Horizontal:
                    v.x = Mathf.Lerp(_container.localPosition.x, _targetPos, centerSpeed * Time.deltaTime);
                    _container.localPosition = v;
                    if (Mathf.Abs(_container.localPosition.x - _targetPos) < 0.01f)
                    {
                        _centering = false;
                    }
                    break;
            }

        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        _centering = true;
        switch (m_AxisType)
        {
            case AxisType.Vertical:
                _targetPos = FindClosestPos(_container.localPosition.y);
                break;
            case AxisType.Horizontal:
                _targetPos = FindClosestPos(_container.localPosition.x);
                break;
        }

    }

    public void OnDrag(PointerEventData eventData)
    {
        _centering = false;
    }

    private float FindClosestPos(float currentPos)
    {
        int childIndex = 0;
        float closest = 0;
        float distance = Mathf.Infinity;

        for (int i = 0; i < _childrenPos.Count; i++)
        {
            float p = _childrenPos[i];
            float d = Mathf.Abs(p - currentPos);
            if (d < distance)
            {
                distance = d;
                closest = p;
                childIndex = i;
            }
        }

        GameObject centerChild = _container.GetChild(childIndex).gameObject;
        if (onCenter != null)
            onCenter(centerChild);

        return closest;
    }
}


附上图片



上图是项目需求的3个垂直Scroll



因为我的表现是垂直的 Anchor的Y轴相等



你可能感兴趣的:(UGUI)