参考:
大神的主页:http://dingxiaowei.cn/2017/04/25/
他的github:https://github.com/dingxiaowei/ScrollRect
改进:
https://blog.csdn.net/chenfujun818/article/details/83545853#comments
ScrollRect拖拽事件和子物体的拖拽事件有冲突的解决办法。
工作中遇到ScrollView的拖拽事件和其子物体下的拖拽事件有冲突,拖拽子物体时左右移动,ScrollView也会动,查CSDN遇到的都是ScrollView的子物体点击事件被拦截的情况,不是拖拽事件。好不容易找到一个讲的也不详细,当时着急实现,所以也不知道文章代码里写的其他内容什么意思。
当时想到的就是根据拖拽时手指的移动方向是沿ScrollView滑动方向还是拖拽方向这个思路,但是不太灵敏,直到我搜了GitHub上的大神写的ScrollView效果大全,里面的一种效果就是我要的效果,就是写的有点复杂了,我就拿来改了改,成了下面这简单版。(文章后附大神的github链接):)
一、相关代码和UI效果。
这个结构就是实现拖拽的全部结构。首先是Canvas的相关设置(包括屏幕适配)。
我这里适配的是1334*750的大小,接下来设置Panel。
Panel设置成这个模式,再根据canvas中的设置,就屏幕适配了。
接下来创建一个Scroll View,因为我不需要显示bar,所以只留Viewport,Scrollbar Horizontal和Scrollbar Vertical全部删除。Scroll View身上的Scroll Rect组件中的Content、Viewport直接拖拽上去,(默认创建Scroll View时就已经添加,检查一下就好)。Movement Type选择Clamped模式(这个自己到时候看需要什么效果吧)。
接下来就是Content身上添加Content Size Fitter组件,选择Horizontal Fit为Preferred Size,作用就是根据Content的子物体的数量调节Content的宽度。
添加Grid Layout Group,设置如图。
保持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图我还没找到合适的软件制作,凑合看吧。
打完收工。