StaggeredGridLayoutManager 中的 itemTouchHelperCallback 中出现混乱的图块

Jumbled tiles in itemTouchHelperCallback in StaggeredGridLayoutManager

我在我的代码中使用 StaggeredGridLayoutManager

先看看这个:

我面临这些问题:

In 1st attempt, I move the tile '1' directly upwards by one step and then stop. Everything looks good and the tile labled as '1' replaces label '2'.

In 2nd attempt, I drag the tile labeled '3' up but do not stop. Still it automatically gets replaced by '1' which is present just above '3'. Something is trouble here.

In my 3rd and 4th attempt, I try to drag my tile up & sideways. Once I do it the tile automatically gets right shifted and out of the frame(even though my touch is still holding it). It can be visible only when I drag it left. Weird!

现在看看我的代码

在我的 MainActivity 的 onCreateMethod

val layoutmanager = StaggeredGridLayoutManager(if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) 3 
                                              else 2, StaggeredGridLayoutManager.VERTICAL)
layoutmanager.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
recyclerView.layoutManager = layoutmanager
noteAdapter = NoteAdapter(myList, this)
recyclerView.adapter = noteAdapter

我的ItemTouchHelperCallback方法

private val itemTouchHelperCallback =
            object :
                    ItemTouchHelper.SimpleCallback(0, 0
                    ) {

                override fun isLongPressDragEnabled(): Boolean {
                    return true
                }

                override fun isItemViewSwipeEnabled(): Boolean {
                    return true
                }

                override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
                    val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or
                            ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
                    val swipeFlags = ItemTouchHelper.START or ItemTouchHelper.END
                    return makeMovementFlags(dragFlags, swipeFlags)
                }

                override fun onMove(
                        recyclerView: RecyclerView,
                        viewHolder: RecyclerView.ViewHolder,
                        target: RecyclerView.ViewHolder
                ): Boolean {
                    val fromPosition = viewHolder.adapterPosition
                    val toPosition = target.adapterPosition

                    if (fromPosition < toPosition) {
                        for (i in fromPosition until toPosition) {
                            Collections.swap(myList, i, i + 1)
                        }
                    } else {
                        for (i in fromPosition downTo toPosition + 1) {
                            Collections.swap(myList, i, i - 1)
                        }
                    }
                    noteAdapter.notifyItemMoved(fromPosition, toPosition)


                    return false
                }
                override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                    
                }

            }

更新

@Zain 发现真正的问题隐藏在我的 NoteAdapter.java 中,所以我强调了错误的方法。检查他的回答。

 @Override
    public int getItemViewType(int position) {
        return position;
    }

问题:

RecyclerView 适配器的 getItemViewType() 返回项目位置;而您只有一种项目视图类型。

简答:

您不应覆盖 getItemViewType() 以将其保持为默认值,或者您可以将其设置为 return 0 以指示适配器只有一个视图类型。

长答案:

当适配器第一次附加到RecyclerView时,RecyclerView的LayoutManager需要知道每个item位置的布局类型;为此,它会为适配器中的每个位置调用 getItemViewType(position);所以如果适配器列表有 10 个项目,那么它被调用 10 次;每次调用时,它 return 就是这个特定项目的类型。

getItemViewType(position) 未被覆盖时,所有项目的 returned 值为 0;这表明所有项目都具有相同的类型。

通过记录 while returning 0:

2021-01-13 11:05:25.941 : getItemViewType:  Pos = 0 Return: 0
2021-01-13 11:05:25.950 : onBindViewHolder: Pos: 0
2021-01-13 11:05:25.954 : getItemViewType:  Pos = 1 Return: 0
2021-01-13 11:05:25.959 : onBindViewHolder: Pos: 1
2021-01-13 11:05:25.962 : getItemViewType:  Pos = 2 Return: 0
2021-01-13 11:05:25.970 : onBindViewHolder: Pos: 2
2021-01-13 11:05:25.974 : getItemViewType:  Pos = 3 Return: 0
2021-01-13 11:05:25.979 : onBindViewHolder: Pos: 3
2021-01-13 11:05:25.982 : getItemViewType:  Pos = 4 Return: 0
2021-01-13 11:05:25.988 : onBindViewHolder: Pos: 4
2021-01-13 11:05:26.000 : getItemViewType:  Pos = 0 Return: 0
2021-01-13 11:05:26.001 : getItemViewType:  Pos = 1 Return: 0
2021-01-13 11:05:26.001 : getItemViewType:  Pos = 2 Return: 0
2021-01-13 11:05:26.001 : getItemViewType:  Pos = 3 Return: 0
2021-01-13 11:05:26.001 : getItemViewType:  Pos = 4 Return: 0

但是你从 getItemViewType(position) return position;那么您拥有的项目类型与您拥有的职位数量(即列表的大小)一样多。

本例日志:

2021-01-13 11:11:30.123 : getItemViewType:  Pos = 0 Return: 0
2021-01-13 11:11:30.145 : onBindViewHolder: Pos: 0
2021-01-13 11:11:30.149 : getItemViewType:  Pos = 1 Return: 1
2021-01-13 11:11:30.160 : onBindViewHolder: Pos: 1
2021-01-13 11:11:30.164 : getItemViewType:  Pos = 2 Return: 2
2021-01-13 11:11:30.172 : onBindViewHolder: Pos: 2
2021-01-13 11:11:30.175 : getItemViewType:  Pos = 3 Return: 3
2021-01-13 11:11:30.179 : onBindViewHolder: Pos: 3
2021-01-13 11:11:30.183 : getItemViewType:  Pos = 4 Return: 4
2021-01-13 11:11:30.190 : onBindViewHolder: Pos: 4
2021-01-13 11:11:30.199 : getItemViewType:  Pos = 0 Return: 0
2021-01-13 11:11:30.199 : getItemViewType:  Pos = 1 Return: 1
2021-01-13 11:11:30.200 : getItemViewType:  Pos = 2 Return: 2
2021-01-13 11:11:30.200 : getItemViewType:  Pos = 3 Return: 3
2021-01-13 11:11:30.200 : getItemViewType:  Pos = 4 Return: 4

现在 RecyclerView 处于稳定状态(即填充了所有项目),我们要将一个项目拖放到另一个项目上。

现在我们将注释 1 替换为 3(即将适配器中的位置 4 替换为位置 2)

getItemViewType()returns 0

时的日志
2021-01-13 11:14:26.620 : getItemViewType:  Pos = 0 Return: 0
2021-01-13 11:14:26.620 : getItemViewType:  Pos = 1 Return: 0
2021-01-13 11:14:26.620 : getItemViewType:  Pos = 2 Return: 0
2021-01-13 11:14:26.621 : getItemViewType:  Pos = 3 Return: 0
2021-01-13 11:14:26.621 : getItemViewType:  Pos = 4 Return: 0

这里的 getItemViewType 每个位置只调用一次,这是因为您只有一个视图类型。并且没有像我们刚刚调用 notifyItemMoved 那样调用 onBindViewHolder,因此 RecyclerView 仅调用获得 cached/recycled 版本,因为在这种情况下所有视图类型都相同。

getItemViewType()returnsposition

时的日志
2021-01-13 11:12:13.659 : getItemViewType:  Pos = 0 Return: 0
2021-01-13 11:12:13.660 : getItemViewType:  Pos = 1 Return: 1
2021-01-13 11:12:13.660 : getItemViewType:  Pos = 2 Return: 2
2021-01-13 11:12:13.660 : getItemViewType:  Pos = 2 Return: 2
2021-01-13 11:12:13.665 : onBindViewHolder: Pos: 2
2021-01-13 11:12:13.667 : getItemViewType:  Pos = 3 Return: 3
2021-01-13 11:12:13.668 : getItemViewType:  Pos = 3 Return: 3
2021-01-13 11:12:13.673 : onBindViewHolder: Pos: 3
2021-01-13 11:12:13.675 : getItemViewType:  Pos = 4 Return: 4
2021-01-13 11:12:13.675 : getItemViewType:  Pos = 4 Return: 4
2021-01-13 11:12:13.681 : onBindViewHolder: Pos: 4

这里 getItemViewType 被多次调用,因为每个项目都有其独特的视图类型,它需要重新计算该视图类型,并使用 [=24 重绘(或重新计算布局)项目=] 对于 from(第 2 项)和 to(第 4 项)项以及介于两者之间的项(第 3 项)。

制作这些multiple view type让你无法完成Drag(可能是新的layout重新计算),也让中间View无法在dragged和dropped-on items之间切换