ScrollRect拖拽事件和子物体的拖拽事件有冲突的解决办法。

工作中遇到ScrollView的拖拽事件和其子物体下的拖拽事件有冲突,拖拽子物体时左右移动,ScrollView也会动,查CSDN遇到的都是ScrollView的子物体点击事件被拦截的情况,不是拖拽事件。好不容易找到一个讲的也不详细,当时着急实现,所以也不知道文章代码里写的其他内容什么意思。
当时想到的就是根据拖拽时手指的移动方向是沿ScrollView滑动方向还是拖拽方向这个思路,但是不太灵敏,直到我搜了GitHub上的大神写的ScrollView效果大全,里面的一种效果就是我要的效果,就是写的有点复杂了,我就拿来改了改,成了下面这简单版。(文章后附大神的github链接):)

一、相关代码和UI效果。

这个结构就是实现拖拽的全部结构。首先是Canvas的相关设置(包括屏幕适配)。

unity同时存在拖拽和点击 unity无法拖拽物体_unity同时存在拖拽和点击


我这里适配的是1334*750的大小,接下来设置Panel。

unity同时存在拖拽和点击 unity无法拖拽物体_ScrollView拖拽_02


Panel设置成这个模式,再根据canvas中的设置,就屏幕适配了。

接下来创建一个Scroll View,因为我不需要显示bar,所以只留Viewport,Scrollbar Horizontal和Scrollbar Vertical全部删除。Scroll View身上的Scroll Rect组件中的ContentViewport直接拖拽上去,(默认创建Scroll View时就已经添加,检查一下就好)。Movement Type选择Clamped模式(这个自己到时候看需要什么效果吧)。

unity同时存在拖拽和点击 unity无法拖拽物体_ScrollView拖拽_03

接下来就是Content身上添加Content Size Fitter组件,选择Horizontal Fit为Preferred Size,作用就是根据Content的子物体的数量调节Content的宽度。

添加Grid Layout Group,设置如图。

unity同时存在拖拽和点击 unity无法拖拽物体_ScrollView拖拽_04


保持Content在屏幕底部就行,接下来在Content下创建子物体,尽量创建多一些。然后写脚本。


上代码,此脚本需要给Content的每个子物体去挂载.

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
//using DG.Tweening;
using System.Collections.Generic;
using System;

public class ImageDrag : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler//, IDropHandler
{
    /// <summary>
    /// 拖拽的偏移量
    /// </summary>
    private Vector3 _touchOffset; 
    /// <summary>
    /// 场景中的Panel,设置拖拽过程中的父物体
    /// </summary>
    private Transform _panel;      
    /// <summary>
    /// Scroll View上的Scroll Rect组件
    /// </summary>
    private ScrollRect _scrollRect; 
    /// <summary>
    /// 拖拽的是否是子物体
    /// </summary>
    private bool _isDragItem;      

    void Awake()
    {

        Input.multiTouchEnabled = false;    //限制多指拖拽

        _panel = this.transform.root.transform.Find("Panel");

        //注意面板中默认创建的ScrollView中间有空格
        _scrollRect = _panel.transform.Find("ScrollView").GetComponent<ScrollRect>();

        _isDragItem = false;

    }

   
    /// <summary>
    /// 开始拖拽
    /// </summary>
    /// <param name="eventData"></param>

    public void OnBeginDrag(PointerEventData eventData)
    {
       
        Vector2 touchDeltaPosition = Vector2.zero;
#if UNITY_EDITOR
        float delta_x = Input.GetAxis("Mouse X");
        float delta_y = Input.GetAxis("Mouse Y");
        touchDeltaPosition = new Vector2(delta_x, delta_y);

#elif UNITY_ANDROID || UNITY_IPHONE
        touchDeltaPosition = Input.GetTouch(0).deltaPosition;  
#endif
        //通过touchDeltaPosition去判断你的手指(鼠标)的移动方向,是和Scroll View同方向还是拖拽的方向
        if (Mathf.Abs(touchDeltaPosition.x) < Mathf.Abs(touchDeltaPosition.y))
        {
            //在这里区分是拖拽Item还是ScrollRect
            _isDragItem = true;

            this.transform.SetParent(_panel);//设置Item的父物体,为什么要在一开始确定是拖拽Item后就设置父物体??你可以注掉试试

            this.transform.SetAsLastSibling();//设置为同父物体的最从底层,也就是不会被其同级遮挡。

            Vector3 uguiPos = new Vector3();   //定义一个接收返回的ugui坐标
            if (RectTransformUtility.ScreenPointToWorldPointInRectangle(this.gameObject.GetComponent<RectTransform>(),
                 eventData.position, Camera.main, out uguiPos))
            {
                //计算图片中心和鼠标点的差值
                _touchOffset = this.transform.position - uguiPos;
            }
        }
        else
        {
            _isDragItem = false;
            if (_scrollRect != null)
                //调用Scroll的OnBeginDrag方法,有了区分,就不会被item的拖拽事件屏蔽
            _scrollRect.OnBeginDrag(eventData);
        }     
    }

    public void OnDrag(PointerEventData eventData)
    {
            //OnDrag的方法都是在OnBeginDrag中区分的。
        if (_isDragItem)
        {
            Vector3 pos;
            if (RectTransformUtility.ScreenPointToWorldPointInRectangle(this.gameObject.GetComponent<RectTransform>(),
                eventData.position, Camera.main, out pos))
            {
                this.transform.position = pos + _touchOffset;
            }
        }
        else
        {
            if (_scrollRect != null)
                _scrollRect.OnDrag(eventData);
        }
    }

    /// <summary>
    /// 结束拖拽
    /// </summary>
    /// <param name="eventData"></param>
    public void OnEndDrag(PointerEventData eventData)
    {
        if (_isDragItem)
        {
            //处理item拖拽结束的逻辑处理
            Debug.Log("--------拖拽子物体结束-----------");
        }
        else
        {
            //Scroll Rect 拖拽结束
            if (_scrollRect != null)
                _scrollRect.OnEndDrag(eventData);
        }

    }
}

这就是简单的解决Scroll View拖拽子物体有冲突的方法,就是在拖拽冲突的Item脚本上去区分是拖拽的Scroll View还是拖拽Item。

效果图就一张图片,gif图我还没找到合适的软件制作,凑合看吧。

unity同时存在拖拽和点击 unity无法拖拽物体_ScrollView拖拽_05


打完收工。