Eliminating the black hole effect with location/drop events. am: 1edc6daf1d
am: 32394cee01
Change-Id: Icbcf0cb248bc823f72fb61373732a08ee56c51cc
This commit is contained in:
@@ -134,6 +134,7 @@ public class DragEvent implements Parcelable {
|
||||
|
||||
Object mLocalState;
|
||||
boolean mDragResult;
|
||||
boolean mEventHandlerWasCalled;
|
||||
|
||||
private DragEvent mNext;
|
||||
private RuntimeException mRecycledLocation;
|
||||
@@ -439,6 +440,7 @@ public class DragEvent implements Parcelable {
|
||||
mClipData = null;
|
||||
mClipDescription = null;
|
||||
mLocalState = null;
|
||||
mEventHandlerWasCalled = false;
|
||||
|
||||
synchronized (gRecyclerLock) {
|
||||
if (gRecyclerUsed < MAX_RECYCLED) {
|
||||
|
||||
@@ -40,7 +40,6 @@ import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Interpolator;
|
||||
import android.graphics.LinearGradient;
|
||||
@@ -20876,6 +20875,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
return callDragEventHandler(event);
|
||||
}
|
||||
|
||||
final boolean callDragEventHandler(DragEvent event) {
|
||||
ListenerInfo li = mListenerInfo;
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (li != null && li.mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
|
||||
|
||||
@@ -153,9 +153,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
*/
|
||||
Transformation mInvalidationTransformation;
|
||||
|
||||
// View currently under an ongoing drag. Can be null, a child or this window.
|
||||
private View mCurrentDragView;
|
||||
|
||||
// Metadata about the ongoing drag
|
||||
private DragEvent mCurrentDragStartEvent;
|
||||
private boolean mIsInterestedInDrag;
|
||||
@@ -1362,16 +1359,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
final float tx = event.mX;
|
||||
final float ty = event.mY;
|
||||
|
||||
ViewRootImpl root = getViewRootImpl();
|
||||
|
||||
// Dispatch down the view hierarchy
|
||||
final PointF localPoint = getLocalPoint();
|
||||
|
||||
switch (event.mAction) {
|
||||
case DragEvent.ACTION_DRAG_STARTED: {
|
||||
// clear state to recalculate which views we drag over
|
||||
mCurrentDragView = null;
|
||||
|
||||
// Set up our tracking of drag-started notifications
|
||||
mCurrentDragStartEvent = DragEvent.obtain(event);
|
||||
if (mChildrenInterestedInDrag == null) {
|
||||
@@ -1434,60 +1426,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
}
|
||||
} break;
|
||||
|
||||
case DragEvent.ACTION_DRAG_LOCATION: {
|
||||
case DragEvent.ACTION_DRAG_LOCATION:
|
||||
case DragEvent.ACTION_DROP: {
|
||||
// Find the [possibly new] drag target
|
||||
View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
|
||||
if (target == null && mIsInterestedInDrag) {
|
||||
target = this;
|
||||
}
|
||||
|
||||
// If we've changed apparent drag target, tell the view root which view
|
||||
// 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(target);
|
||||
|
||||
final int action = event.mAction;
|
||||
// Position should not be available for ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED.
|
||||
event.mX = 0;
|
||||
event.mY = 0;
|
||||
|
||||
// If we've dragged off of a child view or this window, send it the EXITED message
|
||||
if (mCurrentDragView != null) {
|
||||
final View view = mCurrentDragView;
|
||||
event.mAction = DragEvent.ACTION_DRAG_EXITED;
|
||||
if (view != this) {
|
||||
view.dispatchDragEvent(event);
|
||||
view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
|
||||
view.refreshDrawableState();
|
||||
} else {
|
||||
super.dispatchDragEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentDragView = target;
|
||||
|
||||
// If we've dragged over a new child view, send it the ENTERED message, otherwise
|
||||
// send it to this window.
|
||||
if (target != null) {
|
||||
event.mAction = DragEvent.ACTION_DRAG_ENTERED;
|
||||
if (target != this) {
|
||||
target.dispatchDragEvent(event);
|
||||
target.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
|
||||
target.refreshDrawableState();
|
||||
} else {
|
||||
super.dispatchDragEvent(event);
|
||||
}
|
||||
}
|
||||
event.mAction = action; // restore the event's original state
|
||||
event.mX = tx;
|
||||
event.mY = ty;
|
||||
}
|
||||
|
||||
// Dispatch the actual drag location notice, localized into its coordinates
|
||||
// Dispatch the actual drag notice, localized into the target coordinates.
|
||||
if (target != null) {
|
||||
if (target != this) {
|
||||
event.mX = localPoint.x;
|
||||
@@ -1497,58 +1444,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
|
||||
event.mX = tx;
|
||||
event.mY = ty;
|
||||
|
||||
if (!event.mEventHandlerWasCalled && mIsInterestedInDrag) {
|
||||
// The child didn't invoke any event handler, but this view is interested in
|
||||
// drag, so the event goes to this view.
|
||||
retval = super.dispatchDragEvent(event);
|
||||
}
|
||||
} else {
|
||||
retval = super.dispatchDragEvent(event);
|
||||
}
|
||||
}
|
||||
} 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].
|
||||
* If no suitable child is detected, dispatch to this window.
|
||||
*
|
||||
* 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) {
|
||||
final View view = mCurrentDragView;
|
||||
if (view != this) {
|
||||
view.dispatchDragEvent(event);
|
||||
view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
|
||||
view.refreshDrawableState();
|
||||
} else {
|
||||
super.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, localPoint);
|
||||
if (target != null) {
|
||||
if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target);
|
||||
event.mX = localPoint.x;
|
||||
event.mY = localPoint.y;
|
||||
retval = target.dispatchDragEvent(event);
|
||||
event.mX = tx;
|
||||
event.mY = ty;
|
||||
} else if (mIsInterestedInDrag) {
|
||||
retval = super.dispatchDragEvent(event);
|
||||
} else {
|
||||
if (ViewDebug.DEBUG_DRAG) {
|
||||
Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
@@ -1592,6 +1498,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
final boolean canAccept = child.dispatchDragEvent(mCurrentDragStartEvent);
|
||||
mCurrentDragStartEvent.mX = tx;
|
||||
mCurrentDragStartEvent.mY = ty;
|
||||
mCurrentDragStartEvent.mEventHandlerWasCalled = false;
|
||||
if (canAccept) {
|
||||
mChildrenInterestedInDrag.add(child);
|
||||
if (!child.canAcceptDrag()) {
|
||||
|
||||
@@ -5523,9 +5523,8 @@ public final class ViewRootImpl implements ViewParent,
|
||||
if (what == DragEvent.ACTION_DRAG_EXITED) {
|
||||
// A direct EXITED event means that the window manager knows we've just crossed
|
||||
// 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.
|
||||
mView.dispatchDragEvent(event);
|
||||
// just been exited. Send the EXITED notification to the current drag view, if any.
|
||||
setDragFocus(null, event);
|
||||
} else {
|
||||
// For events with a [screen] location, translate into window coordinates
|
||||
if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) {
|
||||
@@ -5548,6 +5547,12 @@ public final class ViewRootImpl implements ViewParent,
|
||||
// Now dispatch the drag/drop event
|
||||
boolean result = mView.dispatchDragEvent(event);
|
||||
|
||||
if (what == DragEvent.ACTION_DRAG_LOCATION && !event.mEventHandlerWasCalled) {
|
||||
// If the LOCATION event wasn't delivered to any handler, no view now has a drag
|
||||
// focus.
|
||||
setDragFocus(null, event);
|
||||
}
|
||||
|
||||
// If we changed apparent drag target, tell the OS about it
|
||||
if (prevDragView != mCurrentDragView) {
|
||||
try {
|
||||
@@ -5575,6 +5580,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
|
||||
// When the drag operation ends, reset drag-related state
|
||||
if (what == DragEvent.ACTION_DRAG_ENDED) {
|
||||
mCurrentDragView = null;
|
||||
setLocalDragState(null);
|
||||
mAttachInfo.mDragToken = null;
|
||||
if (mAttachInfo.mDragSurface != null) {
|
||||
@@ -5634,9 +5640,36 @@ public final class ViewRootImpl implements ViewParent,
|
||||
return mLastTouchSource;
|
||||
}
|
||||
|
||||
public void setDragFocus(View newDragTarget) {
|
||||
public void setDragFocus(View newDragTarget, DragEvent event) {
|
||||
if (mCurrentDragView != newDragTarget) {
|
||||
// Send EXITED and ENTERED notifications to the old and new drag focus views.
|
||||
|
||||
final float tx = event.mX;
|
||||
final float ty = event.mY;
|
||||
final int action = event.mAction;
|
||||
// Position should not be available for ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED.
|
||||
event.mX = 0;
|
||||
event.mY = 0;
|
||||
|
||||
if (mCurrentDragView != null) {
|
||||
event.mAction = DragEvent.ACTION_DRAG_EXITED;
|
||||
mCurrentDragView.callDragEventHandler(event);
|
||||
mCurrentDragView.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
|
||||
mCurrentDragView.refreshDrawableState();
|
||||
}
|
||||
|
||||
mCurrentDragView = newDragTarget;
|
||||
|
||||
if (newDragTarget != null) {
|
||||
event.mAction = DragEvent.ACTION_DRAG_ENTERED;
|
||||
newDragTarget.callDragEventHandler(event);
|
||||
newDragTarget.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
|
||||
newDragTarget.refreshDrawableState();
|
||||
}
|
||||
|
||||
event.mAction = action;
|
||||
event.mX = tx;
|
||||
event.mY = ty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user