From 9d1ab883293b047b654935b84d0803c8c383be91 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 2 Nov 2010 15:55:39 -0700 Subject: Fix drag enter/exit reporting Now, each ViewGroup is tracking which of its child views [which might themselves be ViewGroups] is currently under the drag point, and when the drag leaves that child, a DRAG_EXITED is synthesized and dispatched all the way down to the leaf view previously under the point. ENTERED is still *not* dispatched down like this; instead, it's calculated and synthesized directly at each level based on the new LOCATION. The ViewRoot still tracks the leaf drag target, but solely for the purpose of reporting changes to the OS after full dispatch of a new LOCATION -- the entered/exited messaging is no longer initiated at the ViewRoot level. Change-Id: I0089cc538b7e33a0440187543fcfd2f8b12e197d --- core/java/android/view/ViewGroup.java | 44 +++++++++++++++++++++++++++++++---- core/java/android/view/ViewRoot.java | 17 +++----------- 2 files changed, 43 insertions(+), 18 deletions(-) (limited to 'core') diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 0f9312c..ad343a3 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -870,7 +870,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager switch (event.mAction) { case DragEvent.ACTION_DRAG_STARTED: { // clear state to recalculate which views we drag over - root.setDragFocus(event, null); + mCurrentDragView = null; // Now dispatch down to our children, caching the responses mChildAcceptsDrag = false; @@ -915,11 +915,28 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint); // If we've changed apparent drag target, tell the view root which view - // we're over now. This will in turn send out DRAG_ENTERED / DRAG_EXITED - // notifications as appropriate. + // we're over now [for purposes of the eventual drag-recipient-changed + // notifications to the framework] and tell the new target that the drag + // has entered its bounds. The root will see setDragFocus() calls all + // the way down to the final leaf view that is handling the LOCATION event + // before reporting the new potential recipient to the framework. if (mCurrentDragView != target) { - root.setDragFocus(event, target); + root.setDragFocus(target); + + final int action = event.mAction; + // If we've dragged off of a child view, send it the EXITED message + if (mCurrentDragView != null) { + event.mAction = DragEvent.ACTION_DRAG_EXITED; + mCurrentDragView.dispatchDragEvent(event); + } mCurrentDragView = target; + + // If we've dragged over a new child view, send it the ENTERED message + if (target != null) { + event.mAction = DragEvent.ACTION_DRAG_ENTERED; + target.dispatchDragEvent(event); + } + event.mAction = action; // restore the event's original state } // Dispatch the actual drag location notice, localized into its coordinates @@ -934,6 +951,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } break; + /* Entered / exited dispatch + * + * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is + * that we're about to get the corresponding LOCATION event, which we will use to + * determine which of our children is the new target; at that point we will + * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup]. + * + * DRAG_EXITED *is* dispatched all the way down immediately: once we know the + * drag has left this ViewGroup, we know by definition that every contained subview + * is also no longer under the drag point. + */ + + case DragEvent.ACTION_DRAG_EXITED: { + if (mCurrentDragView != null) { + mCurrentDragView.dispatchDragEvent(event); + mCurrentDragView = null; + } + } break; + case DragEvent.ACTION_DROP: { if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event); View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint); diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 22a7773..c7c2071 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -2493,11 +2493,12 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn // a window boundary, so the current drag target within this one must have // just been exited. Send it the usual notifications and then we're done // for now. - setDragFocus(event, null); + mView.dispatchDragEvent(event); } else { // Cache the drag description when the operation starts, then fill it in // on subsequent calls as a convenience if (what == DragEvent.ACTION_DRAG_STARTED) { + mCurrentDragView = null; // Start the current-recipient tracking mDragDescription = event.mClipDescription; } else { event.mClipDescription = mDragDescription; @@ -2557,22 +2558,10 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn outLocation.y = (int) mLastTouchPoint.y; } - public void setDragFocus(DragEvent event, View newDragTarget) { - final int action = event.mAction; - // If we've dragged off of a view, send it the EXITED message + public void setDragFocus(View newDragTarget) { if (mCurrentDragView != newDragTarget) { - if (mCurrentDragView != null) { - event.mAction = DragEvent.ACTION_DRAG_EXITED; - mCurrentDragView.dispatchDragEvent(event); - } mCurrentDragView = newDragTarget; } - // If we've dragged over a new view, send it the ENTERED message - if (newDragTarget != null) { - event.mAction = DragEvent.ACTION_DRAG_ENTERED; - newDragTarget.dispatchDragEvent(event); - } - event.mAction = action; // restore the event's original state } private AudioManager getAudioManager() { -- cgit v1.1