PSA: Drag-and-Drop Behavior Change in Android 7.0
Dan Lew has run across a couple of cases
where the behavior of Android’s native drag-and-drop APIs differ
between Android 7.0 and earlier versions of Android. If you are using
startDrag() (or the newer
and the like, these behavior changes may be important for you.
I’ll explain the problem as best I can, and I will try to remember to update
this post if Dan posts more about it, beyond the issues that he filed
in the Android issue tracker.
Both issues pertain to nested drop targets, where you are listening for drag
events both on some container and on some view inside of that container.
For example, if you are using the drag-and-drop APIs to support reordering
items in a
RecyclerView, there is a good chance that
you will need to have listeners both on that container and on existing
items in the container (e.g., to animate them out of the way to allow
the user to drop in the newly-vacant spot).
The overarching problem, most simply illustrated in Dan’s first issue, is that drag events are inclusive on Android 6.0 and earlier and exclusive on Android 7.0.
Suppose that we have the following portion of a layout:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout android:id="@+id/outer_container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/outer_normal" android:padding="48dp"> <FrameLayout android:id="@+id/inner_container" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/inner_normal" android:padding="48dp"> <ImageView android:id="@+id/thumbnail_large" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/image_normal" android:contentDescription="@string/icon" android:scaleType="centerInside" /> </FrameLayout> </FrameLayout>
For various reasons, we have hooked up event listeners for all
three things in the layout: both
FrameLayout containers and the
ImageView. These are nested, with one
FrameLayout holding the
FrameLayout, which holds the
On Android 6.0 and earlier, drag events are inclusive. In other words,
if the user has dragged an item into the inner
is also considered to be inside the outer
FrameLayout. From an event
standpoint, the outer
FrameLayout only gets an
event when the dragged item leaves its outer boundaries.
However, on Android 7.0, drag events are exclusive. If the user drags
an item into the inner
FrameLayout, the item will exit the outer
FrameLayout from a drag-and-drop perspective.
So, whereas on Android 6.0, we get this:
…on Android 7.0, we get this, from the same code:
So, you get different events based on Android version. Which one is “right” is somewhat immaterial, in that existing code will not behave the same, which is why these sorts of undocumented behavior changes are unfortunate.
Dan’s second issue
is along the same lines: a parent container stops receiving events when
the dragged item is over a child. This particular issue is a bit more
complex, involving one of the containers registering for drag events
ACTION_DRAG_STARTED (indicating that it is
not interested in further events for this drag operation). It also misses
a glorious opportunity to open an issue report with “Two
EditText walk into a bar”.
If you are using the drag-and-drop APIs, be sure to test your app
thoroughly on Android 7.0, as clearly some changes were made to the
implementation. If you use nested drop targets, as in these issues,
you really need to test your app thoroughly, so you can adjust your
behavior. In the sample app
that I wrote to illustrate the problem, I also have a
helper class that helps get Android 7.0 to behave more like Android 6.0,
with inclusive drag events, though that code has not been tested beyond
the sample app.
If you happen to be coming to droidcon NYC, you can thank Dan in person for his bug sleuthing, as part of attending his talk on efficient Android layouts. I’ll be there as well, talking about drag-and-drop, including covering this behavior change.
Stuck on an Android problem? Subscribers have access to live office hours chats with Mark Murphy, to help you work through your challenges!