DragSortAdapter icon indicating copy to clipboard operation
DragSortAdapter copied to clipboard

Weird behavior with multiple RecyclerViews and DragSortAdapters

Open Harti opened this issue 8 years ago • 2 comments

Edit: I proposed a (working) solution that addresses my problem, see 68ad231. (However, I messed up the datatype, it should be RecyclerView.ViewHolder instead of ViewHolder, so I can't make a PR).

One of my app's screens is supposed to use nested RecyclerViews, each with DragSort functionality.

Disclaimer

Please don't unleash the lists-in-lists-hate on me, it does have its purpose and I am aware that this may confuse users (which is why I disabled scrolling for the child RecyclerViews). While my View hierarchy may certainly be disputable, I still think this is worth a bug report. I'm open to refactoring suggestions regarding my View hierarchy, but I probably won't be able to handle everything in one list (with indent levels and such) because the DragSorting will get messed up.

Problem description

Under certain circumstances (see below, "Reproducing the crash") I get the following exception:

java.lang.NullPointerException: Attempt to invoke virtual method 'int android.support.v7.widget.RecyclerView$ViewHolder.getAdapterPosition()' on a null object reference
            at com.makeramen.dragsortadapter.DragManager.onDrag(DragManager.java:36)
            at android.view.View.dispatchDragEvent(View.java:18321)
            at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1492)
            at android.view.ViewGroup.notifyChildOfDrag(ViewGroup.java:1523)
            at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:1365)
            at android.view.ViewGroup.notifyChildOfDrag(ViewGroup.java:1523)
            ...           
            at android.view.ViewRootImpl.handleDragEvent(ViewRootImpl.java:5073)
            ...

The ViewGroup methods get called a dozen times because the DragEvent is being passed down from the very root View down to the View hierarchy's very "leaves".

View hierarchy

This is what my View looks like: illustration_1

The child list with the yellow background is a non-scrollable RecyclerView ("NoScrollRecyclerView") that's being attached to parent list item [43] by my root list's Adapter. Each RV (be it root or child) has its own distinct DragSortAdapter (yup, I double checked this).

Debugging steps

I spent the last five hours in the Android Studio debugger, trying to find whether there is something wrong on on my end, I quadruple checked the itemIds being assigned to the respective ViewHolders, but everything seems fine.

The NullPointer derives from the fact that this library will sometimes find the wrong RecyclerView, therefore recyclerView.findViewHolderForItemId(itemId) will not find the item because it doesn't hold that ViewHolder. For some reason, the DragManager doesn't know which RecyclerView's element is being dragged. I went to investigate deeper.

As you can see in the stack trace, the DragEvent is being dispatched for all children of the application's very root view, and it will bubble all the way down to the (child) NoScrollRecyclerViews in my layout, even though dragStart() and the DragEvent were triggered from within the (root) RecyclerView.

Reproducing the crash

The NullPointer occurs when:

  • I drag a root item while there is a child list around.
  • I drag any item while there are multiple child lists around.

The NullPointer does NOT occur when:

  • I drag a root item while there are no child lists.
  • I drag a child item within the only child list.

The NullPointer occurs because the View passed to DragManager.onDrag() by the Android Framework is the wrong RV, except when there is only one around. That may be by design, however, I don't know how to fix this.

Taking action

As a consequence I tried to extend the DragManager to check the assignment for null and simply do nothing if that's the case. However, the decompiled DragManager class is declared final and also isn't publically visible from external packages, therefore I was unable to patch this behavior by doing that. (I don't even know if it would behave properly when the crash would not occur.)

I also tried overriding the onDrag callback in the NoScrollRecyclerView.

I considered overriding findViewHolderForItemId, but I'm not sure what I am supposed to return if not null. Funny thing is, Android's documentation comment above this method states that, if you use setHasStableIds(true) it would return non-null values. That also did not work.

TL;DR

Without exposing literally all of my code, it's really hard to explain the issue. You may or may not consider doing something about this - since I am using unorthodox View hierarchies.

Thanks for taking your time to help me out. I'm open to refactoring suggestions regarding my View hierarchy.

Harti avatar Jul 29 '15 11:07 Harti

@Harti IllegalStateException("Drag shadow dimensions must not be negative") is throwed from startDrag() of DragSortAdapter. It can be related problem with your.

KirillMakarov avatar Jan 24 '16 18:01 KirillMakarov

@Harti Your fix worked very well, I am also using the DraggableSortAdapter with multiple RecyclerViews. How can we have this pull request created and merged in?

sorter avatar Mar 26 '18 20:03 sorter