RecyclerView StaggeredGridLayoutManager重新排序问题

Mar*_*say 16 android recycler-adapter android-recyclerview

我试图在RecyclerView一个StaggeredGridLayoutManager带有两列的a中显示三个(至少是我遇到问题的情况).第一项跨越两行.这是它的样子:

正确的初始行为

现在,我将项目"项目2"移到顶部.这是我在适配器中调用的代码(这是我编写的一个示例,用于演示我在一个更复杂的项目中遇到的问题):

private int findById(int id) {
    for (int i = 0; i < items.size(); ++i) {
        if (items.get(i).title.equals("Item " + id)) {
            return i;
        }
    }

    return -1;
}

// Moving the item "Item 2" with id = 2 and position = 0
public void moveItem(int id, int position) {
    final int idx = findById(id);
    final Item item = items.get(idx);

    if (position != idx) {
        items.remove(idx);
        items.add(position, item);
        notifyItemMoved(idx, position);
        //notifyDataSetChanged();
    }
}
Run Code Online (Sandbox Code Playgroud)

之后,阵列很好:[Item 2, Item 1, Item 3].但是,观点远非如此:

布局问题

如果我触摸RecyclerView(足够触发过卷效果,如果没有足够的项目可以滚动),则项目2向左移动,我希望在第一个位置看到它(带有漂亮的动画):

预期结果

正如您在代码中看到的那样,我试图notifyItemMoved(idx, position)通过调用替换notifyDataSetChanged().它有效,但变化不是动画.

我写了一个完整的示例来演示这个并将它放在GitHub上.它几乎是最小的(有移动项目和切换其跨越的选项).

我不明白我做错了什么.这是一个错误StaggeredGridLayoutManager吗?我想避免,notifyDataSetChanged()因为我想保持动画的一致性.


编辑:经过一些挖掘,不需要一个完全跨越的项目来显示问题.我删除了全跨度.当我尝试将第2项移动到位置0时,它不会移动:第1项跟在它后面,第3项在右边移动,所以我有:空单元格,第2项,新行,第1项,第3项滚动后我仍然有正确的布局.

更有趣的是我没有问题GridLayoutManager.我需要一个全范围的项目,所以它不是一个解决方案,但我想这确实是一个错误StaggeredGridLayoutManager...

Moj*_*osh 12

我没有完整的答案,但我可以指出你的解决方法和错误报告(我相信是相关的).

诀窍更新布局,使它看起来像你的第二个屏幕,是调用invalidateSpanAssignments()StaggeredGridLayoutManger(sglm)你叫后notifyItemMoved()."挑战"是,如果你立即调用它nIM(),它将无法运行.如果你将呼叫延迟几毫秒,它就会.因此,在您引用的MainActivity代码中,我创建sglm了一个私有字段:

private StaggeredGridLayoutManager sglm;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    adapter = new Adapter();
    recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    sglm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
    recyclerView.setLayoutManager(sglm);
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.setAdapter(adapter);
}
Run Code Online (Sandbox Code Playgroud)

在switch块中,在一个处理程序中引用它:

        case R.id.move_sec_top:
            adapter.moveItem(2, 0);
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    sglm.invalidateSpanAssignments();
                }
            }, 100);
            return true;
Run Code Online (Sandbox Code Playgroud)

结果是您的动画仍然运行,布局以您希望的方式结束.这是一个真正的kludge,但它确实有效.我相信这是我在以下链接中发现并报告的相同错误:

https://code.google.com/p/android/issues/detail?id=93156

虽然我的"症状"和所需的呼叫是不同的,但潜在的问题似乎是相同的.

祝好运!

编辑:无需postdelayed,只需发布​​即可:

        case R.id.move_sec_top:
            adapter.moveItem(2, 0);
            new Handler().post(new Runnable() {
                @Override
                public void run() {
                    sglm.invalidateSpanAssignments();
                }
            });
            return true;
Run Code Online (Sandbox Code Playgroud)

我最初的理论是,在布局通过结束之前,通话被阻止了,但我相信情况并非如此.相反,我现在认为,如果你invalidateSpanAssignments()立即调用,它实际上执行得太快(在布局更改完成之前).因此,上面的帖子(没有延迟)只是将调用添加到渲染队列的末尾,在布局之后它发生.