在Unity3D中的ScrollRect(ScrollView)中拖放

时间:2017-06-13 07:56:56

标签: unity3d drag-and-drop scrollview eventtrigger scrollrect

我想在滚动视图的内容上实现拖放。

问题是当您尝试在滚动视图中拖动项目时,您无法滚动视图。

首先,我尝试通过IDragHandler,IBeginDragHandler,IEndDragHandle和IDropHandler接口实现拖放。初看起来,它工作得很好,但问题是你无法滚动ScrollRect。

我认为问题是由于覆盖,当我使用与拖动一样滚动矩形的事件触发器时,父项不能正常工作。

所以在那之后,我自己想到并通过IPointerDown实现它,IPointerUp接口和在ScrollRect中保存拖拽UI的特定时间,如果你不在特定的时间内保持滚动工作好。

但问题是启用我在OnDrag之前编写的DragHandler脚本,OnBeginDrag和OnEndDrag函数在持有时间结束时不起作用。

首先我想知道有什么方法可以调用这些函数吗?

第二种是有没有办法在不使用拖动界面的情况下实现拖放UI?

DragHandler:

using System;
using UnityEngine;
using UnityEngine.EventSystems;

public class DragHandler : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
{
    public static GameObject itemBeingDragged;

    private Vector3 startPos;

    private Transform startParent;

    DragHandler dragHandler;

    public void Awake()
    {
        dragHandler = GetComponent<DragHandler>();
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        Debug.Log("Begin");
        itemBeingDragged = gameObject;
        startPos = transform.position;
        startParent = transform.parent;
    }

    public void OnDrag(PointerEventData eventData)
    {
        Debug.Log("Drag");
        transform.position = Input.mousePosition;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        Debug.Log("End");
        itemBeingDragged = null;
        if (transform.parent == startParent)
        {
            dragHandler.enabled = false;
            transform.SetParent(startParent);
            transform.position = startPos;
        }
    }
}

ScrollRectController:

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

public class ScrollRectController : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
    public float holdTime;
    public float maxVelocity;

    private Transform scrollRectParent;

    private DragHandler dragHandler;

    private ScrollRect scrollRect;

    private float timer;

    private bool isHolding;

    void Awake()
    {
        scrollRectParent = GameObject.FindGameObjectWithTag("rec_dlg").transform;
        dragHandler = GetComponent<DragHandler>();
        dragHandler.enabled = false;
    }

    // Use this for initialization
    void Start()
    {
        timer = holdTime;
    }

    // Update is called once per frame
    void Update()
    {

    }

    public void OnPointerDown(PointerEventData eventData)
    {
        Debug.Log("Down");
        scrollRect = scrollRectParent.GetComponent<ScrollRect>();
        isHolding = true;
        StartCoroutine(Holding());
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        Debug.Log("Up");
        isHolding = false;
    }

    IEnumerator Holding()
    {
        while (timer > 0)
        {
            //if (scrollRect.velocity.x >= maxVelocity)
            //{
            //    isHolding = false;
            //}

            if (!isHolding)
            {
                timer = holdTime;
                yield break;
            }

            timer -= Time.deltaTime;
            Debug.Log(timer);
            yield return null;
        }

        dragHandler.enabled = true;
        //dragHandler.OnBeginDrag();
    }
}

插槽:

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

public class Slot : MonoBehaviour, IDropHandler
{
    public void OnDrop(PointerEventData eventData)
    {
        DragHandler.itemBeingDragged.transform.SetParent(transform);
    }
}

3 个答案:

答案 0 :(得分:5)

<强>答案:

我编写了一些代码来处理scrollRect(scrollView)中的拖放而不使用DragHandler接口。

DragHandler:

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

public class DragHandler : MonoBehaviour, IPointerExitHandler
{
    public static GameObject itemBeingDragged;

    public static bool isCustomerDragged;

    public Transform customerScrollRect;
    public Transform dragParent;

    public float holdTime;
    public float maxScrollVelocityInDrag;

    private Transform startParent;

    private ScrollRect scrollRect;

    private float timer;

    private bool isHolding;
    private bool canDrag;
    private bool isPointerOverGameObject;

    private CanvasGroup canvasGroup;

    private Vector3 startPos;

    public Transform StartParent
    {
        get { return startParent; }
    }

    public Vector3 StartPos
    {
        get { return startPos; }
    }

    void Awake()
    {
        canvasGroup = GetComponent<CanvasGroup>();
    }

    // Use this for initialization
    void Start()
    {
        timer = holdTime;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            if (EventSystem.current.currentSelectedGameObject == gameObject)
            {
                //Debug.Log("Mouse Button Down");
                scrollRect = customerScrollRect.GetComponent<ScrollRect>();
                isPointerOverGameObject = true;
                isHolding = true;
                StartCoroutine(Holding());
            }
        }

        if (Input.GetMouseButtonUp(0))
        {
            if (EventSystem.current.currentSelectedGameObject == gameObject)
            {
                //Debug.Log("Mouse Button Up");
                isHolding = false;

                if (canDrag)
                {
                    itemBeingDragged = null;
                    isCustomerDragged = false;
                    if (transform.parent == dragParent)
                    {
                        canvasGroup.blocksRaycasts = true;
                        transform.SetParent(startParent);
                        transform.localPosition = startPos;
                    }
                    canDrag = false;
                    timer = holdTime;
                }
            }
        }

        if (Input.GetMouseButton(0))
        {
            if (EventSystem.current.currentSelectedGameObject == gameObject)
            {
                if (canDrag)
                {
                    //Debug.Log("Mouse Button");
                    transform.position = Input.mousePosition;
                }
                else
                {
                    if (!isPointerOverGameObject)
                    {
                        isHolding = false;
                    }
                }
            }
        }
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        isPointerOverGameObject = false;
    }

    IEnumerator Holding()
    {
        while (timer > 0)
        {
            if (scrollRect.velocity.x >= maxScrollVelocityInDrag)
            {
                isHolding = false;
            }

            if (!isHolding)
            {
                timer = holdTime;
                yield break;
            }

            timer -= Time.deltaTime;
            //Debug.Log("Time : " + timer);
            yield return null;
        }

        isCustomerDragged = true;
        itemBeingDragged = gameObject;
        startPos = transform.localPosition;
        startParent = transform.parent;
        canDrag = true;
        canvasGroup.blocksRaycasts = false;
        transform.SetParent(dragParent);
    }

    public void Reset()
    {
        isHolding = false;
        canDrag = false;
        isPointerOverGameObject = false;
    }
}

对这段代码的一些解释:

  1. 你的可拖动UI元素需要难以处理的选项,对我来说,我使用了按钮。
  2. 您需要将此脚本附加到可拖动项目。
  3. 您还需要添加Canvas Group组件。
  4. customerScrollRect是您商品的ScrollRect父级。
  5. dragParent可以是由于视口的掩码而使用的空GameObject。
  6. 插槽:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.EventSystems;
    
    public class Slot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
    {
        bool isEntered;
    
        public void OnPointerEnter(PointerEventData eventData)
        {
            isEntered = true;
        }
    
        public void OnPointerExit(PointerEventData eventData)
        {
            isEntered = false;
        }
    
        void Update()
        {
            if (Input.GetMouseButtonUp(0))
            {
                if (isEntered)
                {
                    if (DragHandler.itemBeingDragged)
                    {
                        GameObject draggedItem = DragHandler.itemBeingDragged;
                        DragHandler dragHandler = draggedItem.GetComponent<DragHandler>();
                        Vector3 childPos = draggedItem.transform.position;
                        //Debug.Log("On Pointer Enter");
                        draggedItem.transform.SetParent(dragHandler.StartParent);
                        draggedItem.transform.localPosition = dragHandler.StartPos;
                        draggedItem.transform.parent.SetParent(transform);
                        draggedItem.transform.parent.position = childPos;
                        isEntered = false;
                    }
                }
            }
        }
    }
    

    此脚本的一些解释:

    1.将脚本附加到已删除的项目。

答案 1 :(得分:1)

我设法找到了一个更简单的解决方案。 也许必须根据特殊需要对其进行自定义,但是如果将此脚本放在您的商品上并进行设置,它应该可以工作。

从预制件上很难做到,但我让控制器来做到这一点。 我在指定的GameObject中找到所有UIElementDragger,并以编程方式添加所需的GO实例。

但是,如果您不使用预制件,则可以立即使用此代码。

using UnityEngine;
using UnityEngine.EventSystems;

public class UIElementDragger : MonoBehaviour, IPointerUpHandler, IPointerDownHandler
{
    /// <summary>
    /// Offset in pixels horizontally (positive to right, negative to left)
    /// </summary>
    [Range(40, 100)]
    public float offsetX = 40;

    /// <summary>
    /// Offset in pixels vertically (positive to right, negative to left)
    /// </summary>
    [Range(40, 100)]
    public float offsetY = 40;

    /// <summary>
    /// The Panel where the item will set as Child to during drag
    /// </summary>
    public Transform parentRect;

    /// <summary>
    /// The GameObject where the item is at start
    /// </summary>
    public Transform homeWrapper;

    /// <summary>
    /// The Object where the mouse must be when pointer is up, to put it in this panel
    /// </summary>
    public Transform targetRect;

    /// <summary>
    /// The GameObject where the item should live after dropping
    /// </summary>
    public Transform targetWrapper;

    private int siblingIndex;
    private bool dragging;

    private void Start()
    {
        siblingIndex = transform.GetSiblingIndex();
    }

    private void Update()
    {
        if (dragging)
        {
            transform.position = new Vector2(Input.mousePosition.x + offsetX, Input.mousePosition.y + offsetY);
        }
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        transform.parent = parentRect;
        dragging = true;
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        if (eventData.pointerCurrentRaycast.gameObject.transform.IsChildOf(targetRect))
        {
            transform.parent = targetWrapper;
        }
        else
        {
            transform.parent = homeWrapper;
            transform.SetSiblingIndex(siblingIndex);
        }

        dragging = false;
    }
}

答案 2 :(得分:1)

此问题最简单的解决方法实际上是使用 ExecuteEvents.Execute 手动调用 ScrollRect的事件 IF 。 >。该解决方案具有最少的附加代码。

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

public class DragAndDropHandler : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler, IDragHandler, IBeginDragHandler, IEndDragHandler
{
    public bool Draggable { get; set; }

    private bool draggingSlot;

    [SerializeField] private ScrollRect scrollRect;

    public void OnPointerDown(PointerEventData eventData)
    {
        if (!Draggable)
        {
            return;
        }

        StartCoroutine(StartTimer());
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        StopAllCoroutines();
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        StopAllCoroutines();
    }

    private IEnumerator StartTimer()
    {
        yield return new WaitForSeconds(0.5f);
        draggingSlot = true;
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        ExecuteEvents.Execute(scrollRect.gameObject, eventData, ExecuteEvents.beginDragHandler);
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (draggingSlot)
        {
            //DO YOUR DRAGGING HERE
        } else
        {
            //OR DO THE SCROLLRECT'S
            ExecuteEvents.Execute(scrollRect.gameObject, eventData, ExecuteEvents.dragHandler);
        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        ExecuteEvents.Execute(scrollRect.gameObject, eventData, ExecuteEvents.endDragHandler);
        if (draggingSlot)
        {
            //END YOUR DRAGGING HERE
            draggingSlot = false;
        }
    }
}