drag分发分析

DragEvent的类型有如下几种:

ACTION_DRAG_STARTED 拖动刚开始时,如果能接收,那么后续就会接收到相关的action.但是如果不接收,那么后续的action将接收不到

ACTION_DRAG_LOCATION 在view中移动时

ACTION_DROP 当拖动放手时,执行

ACTION_DRAG_ENDED 当拖动drop时,凡是能够接收start的,都会执行end,但是drop只会在当时drop的view响应

ACTION_DRAG_ENTERED 如果view能够接收start,那么当拖动的shadow进入view的bound时,会执行

ACTION_DRAG_EXITED 拖动的shadow离开view的bound时, 那么后续的ACTION_DRAG_LOCATION将不会接收

ACTION_DRAG_PRE_ENDED

这部分的分析从DecorView传递到ViewGroup开始:

执行到View.dispatchDragEvent:
根据DragEvent的action类型进行不同的逻辑处理

(1)case DragEvent.ACTION_DRAG_STARTED:
1)遍历当前ViewGroup的子view, 清理child的mPrivateFlag2,并且的当前view可见,则执行

notifyChildOfDragStart(child){

final float[] point = getTempPoint();
point[0] = tx;
point[1] = ty;
transformPointToViewLocal(point, child);//根据scroll值修正位置

final boolean canAccept = child.dispatchDragEvent(mCurrentDragStartEvent);
//canAccept 为true , 说明当前的view对这个drag事件感兴趣 ,那么将
// 当前view放在mChildrenInterestedInDrag数据结构中保存
if (canAccept) {
    mChildrenInterestedInDrag.add(child);
    if (!child.canAcceptDrag()) {
        child.mPrivateFlags2 |= View.PFLAG2_DRAG_CAN_ACCEPT;
        child.refreshDrawableState();
    }
}

}

2)判断该次DragEvent是否有view感兴趣

if (!retval) {
// Neither us nor any of our children are interested in this drag, so stop tracking
// the current drag event.
mCurrentDragStartEvent.recycle();//如果没有view对这个drag start事件感兴趣,那么该DragEvent就会被回收掉
mCurrentDragStartEvent = null;
}

(2)case DragEvent.ACTION_DRAG_ENDED:

根据事前保存的对drag感兴趣的view,依次分发该event, 然后清理掉该兴趣的view, 确保这次拖拽的周期完成

(3)case DragEvent.ACTION_DRAG_LOCATION:

case DragEvent.ACTION_DROP:

//找到当前drag位置的view
View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
//遍历 所有子view ,调用到isTransformedTouchPointInView  通过event的位置 , 确定view
//mCurrentDragChild 是上一次的view
//判断当前位置的view与上次的view是否相同
if (target != mCurrentDragChild){
// 不相同,如果上一次的view不为空 , 
//那么如果对于上次的view,这个时候已经是ACTION_DRAG_EXITED,对于这次的目标view就是ACTION_DRAG_ENTERED
//调整逻辑如下
    if (mCurrentDragChild != null) {
        event.mAction = DragEvent.ACTION_DRAG_EXITED;
        mCurrentDragChild.dispatchDragEnterExitInPreN(event);
    }

    if (target != null) {
        event.mAction = DragEvent.ACTION_DRAG_ENTERED;
        target.dispatchDragEnterExitInPreN(event);
    }
  ...
}

上面分析的是ViewGroup这一层的分析逻辑

下面会分析下View这一次具体的处理逻辑

调到View.dispatchDragEvent

public boolean dispatchDragEvent(DragEvent event) {
    event.mEventHandlerWasCalled = true;
    if (event.mAction == DragEvent.ACTION_DRAG_LOCATION ||
        event.mAction == DragEvent.ACTION_DROP) {
        // About to deliver an event with coordinates to this view. Notify that now this view
        // has drag focus. This will send exit/enter events as needed.
        getViewRootImpl().setDragFocus(this, event);
        //setDragFocus() 锁定view,如果当前测view与目标view不匹配了.
        //那么就需要重新分发ACTION_DRAG_EXITED和ACTION_DRAG_ENTERED
        // 如果是匹配的,就只保存目标view到当前view
    }
    return callDragEventHandler(event);
}

//具体的处理逻辑,当给当前的view设置了OnDragListener时,就回调到OnDragListener的onDrag方法 ,
//如果onDrag返回true,表示已经消费了该action那么就不调用,onDragEvent
//如果返回false,表示没有消费该action,则会调用到onDragEvent

final boolean callDragEventHandler(DragEvent event) {
    final boolean result;

    ListenerInfo li = mListenerInfo;
    //noinspection SimplifiableIfStatement
    if (li != null && li.mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
            && li.mOnDragListener.onDrag(this, event)) {
        result = true;
    } else {
        result = onDragEvent(event);
    }

    switch (event.mAction) {
        case DragEvent.ACTION_DRAG_ENTERED: {
            mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
            refreshDrawableState();
        } break;
        case DragEvent.ACTION_DRAG_EXITED: {
            mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
            refreshDrawableState();
        } break;
        case DragEvent.ACTION_DRAG_ENDED: {
            mPrivateFlags2 &= ~View.DRAG_MASK;
            refreshDrawableState();
        } break;
    }

    return result;
}

results for ""

    No results matching ""