起因
最近项目需要用到 Drag Icon,同时也是 ScrollRect 的组件,但是 Icon 的拖拽事件和 ScrollRect 的拖拽事件相互冲突
刚好看到了上面链接的例子,但是上面的例子有些不跟手,我把代码统一了一下接口,并加入新的跟随,方便日后新的组件使用
思路
这里的思路非常简单,如果需要拖拽 ScrollRect 下的 Icon,那么在 Icon 对应的拖拽开始、拖拽、拖拽结束,3个事件中加入判断,如果是拖拽Icon,则调用拖拽Icon逻辑,如果是拖拽 ScrollRect,则调用 ScrollRect 对应的事件
代码提供 Icon 拖拽时 3个方法的重载,实现时直接 override 即可
PS:原文作者在 Awake 中初始化代码中的组件,这里我全部使用检视面板中拖拽的方式,否则当 Icon创建较多时,每个组件都会挨个去找父物体的节点,不如在编译时直接保存
组件代码
[DisallowMultipleComponent]
public class IDragableScrollViewItem : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler {
public bool dragOnSurfaces = true;
public Transform DragParent;
public Transform OriginParent;
public ScrollRect Scroll;
private Transform _drag;
private bool _isDragItem;
private Vector3 _touchOffset;
private RectTransform _draggingPlanes;
private void Awake() { InitializeData(); }
protected virtual void InitializeData() {
Input.multiTouchEnabled = false;
_isDragItem = false;
_drag = transform;
}
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
if(Mathf.Abs(touchDeltaPosition.x) < Mathf.Abs(touchDeltaPosition.y)){
_isDragItem = true;
_drag.SetParent(DragParent);
_drag.SetAsLastSibling();
OnItemBeginDrag();
SetDraggedPosition(eventData);
}
else{
_isDragItem = false;
Scroll?.OnBeginDrag(eventData);
}
}
public void OnDrag(PointerEventData eventData) {
if(_isDragItem){
SetDraggedPosition(eventData);
OnItemDrag();
}
else{ Scroll?.OnDrag(eventData); }
}
public void OnEndDrag(PointerEventData eventData) {
if(_isDragItem){
_drag.SetParent(OriginParent);
(_drag as RectTransform).anchoredPosition = Vector2.zero;
OnItemEndDrag();
}
else{ Scroll?.OnEndDrag(eventData); }
}
protected virtual void OnItemEndDrag() { }
protected virtual void OnItemDrag() { }
protected virtual void OnItemBeginDrag() { }
private void SetDraggedPosition(PointerEventData eventData) {
if(dragOnSurfaces &&
eventData.pointerEnter != null &&
eventData.pointerEnter.transform as RectTransform != null){
_draggingPlanes = eventData.pointerEnter.transform as RectTransform;
}
var rt = _drag.transform as RectTransform;
Vector3 globalMousePos;
if(RectTransformUtility.ScreenPointToWorldPointInRectangle(
_draggingPlanes,
eventData.position,
eventData.pressEventCamera,
out globalMousePos
)){ rt.position = globalMousePos; }
}
}
使用参考
这里因为我的 Icon 需要捕获 Drop 事件,所以在开始和结束时,需要取消对射线的 Block
public class DragExample : IDragableScrollViewItem {
public Image Icon;
protected override void OnItemBeginDrag() {
Icon.raycastTarget = false;
}
protected override void OnItemDrag() {
}
protected override void OnItemEndDrag() {
Icon.raycastTarget = true;
}
}