Android RecyclerView LayoutManager重新排序问题

Android RecyclerView LayoutManager重新排序问题,android,android-recyclerview,Android,Android Recyclerview,我试图在一个带有两列的StaggedGridLayoutManager的RecyclerView中显示三个项目(至少我有问题)。第一项跨越两行。下面是它的样子: 现在,我将项目“项目2”移到顶部。下面是我在适配器中调用的代码(这是我编写的一个示例,用于演示我在更复杂的项目中遇到的问题): private int findById(int id){ 对于(int i=0;i

我试图在一个带有两列的
StaggedGridLayoutManager的
RecyclerView
中显示三个项目(至少我有问题)。第一项跨越两行。下面是它的样子:

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

private int findById(int id){
对于(int i=0;i
之后,数组就可以了:
[项目2、项目1、项目3]
。然而,这一观点远非完美:

如果我触摸
RecyclerView
(如果没有足够的项目滚动,足以触发overscroll效果),项目2移到左侧,我希望首先看到它(有一个漂亮的动画):

正如您可能在代码中看到的,我试图通过调用
notifyDataSetChanged()
来替换
notifyItemMoved(idx,position)
。它可以工作,但更改未设置动画

我写了一个完整的例子来证明这一点,并把它。这几乎是最小的(有移动项目和切换其范围的选项)

我看不出我做错了什么。这是
StaggedGridLayoutManager的错误吗?我希望避免
notifyDataSetChanged()
,因为我希望保持动画的一致性


编辑:经过一些挖掘,不需要一个完整的项目来显示问题。我拆下了整个跨度。当我尝试将项目2移动到位置0时,它不会移动:项目1在它之后移动,项目3在右侧移动,因此我有:空单元格、项目2、新行、项目1、项目3。我仍然有正确的布局后,滚动


更有趣的是,我没有使用
GridLayoutManager
的问题。我需要一个完整的项目,所以它不是一个解决方案,但我想它确实是
StaggedGridLayoutManager
中的一个错误。…

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

更新布局使其看起来像您的第二个屏幕截图的诀窍是,在调用
notifyItemMoved()
后,在
StaggedGridLayoutManager
(sglm)上调用
invalidateSpanAssignments()
。“挑战”是如果在
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);
}
在开关块中,在处理程序中引用它:

        case R.id.move_sec_top:
            adapter.moveItem(2, 0);
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    sglm.invalidateSpanAssignments();
                }
            }, 100);
            return true;
结果是动画仍在运行,布局以您想要的方式结束。这是一个真正的难题,但它确实有效。我相信这与我在以下链接中发现并报告的错误相同:

虽然我的“症状”和要求的电话不同,但根本问题似乎是相同的

祝你好运

编辑:无需延迟发布,只需发布即可:

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

我最初的理论是,在布局传递结束之前,呼叫一直被阻止,但我认为情况并非如此。相反,我现在认为,如果您立即调用
invalidateSpanAssignments()
,它实际上执行得太快(在布局更改完成之前)。因此,上面的帖子(毫不延迟)只是将调用添加到渲染队列的末尾,在布局之后调用就发生在那里。

我已经这样做了

StaggeredGridLayoutManager gaggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
gaggeredGridLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
recyclerView.setLayoutManager(gaggeredGridLayoutManager);

dataList = YourDataList (Your Code for Arraylist);

recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerAdapter = new DataAdapter(dataList, recyclerView);
recyclerView.setAdapter(recyclerAdapter);

// Magic line
recyclerView.addOnScrollListener(new ScrollListener());
为自定义回收视图滚动侦听器创建类

private class ScrollListener extends RecyclerView.OnScrollListener {
   @Override
   public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        gaggeredGridLayoutManager.invalidateSpanAssignments();
   }
}

希望这能对您有所帮助。

我不知道这是否是一个bug,但我也看到了这一点,当您滚动时,事情似乎在移动,
gap策略
正在运行checked@tyczj:
GAP\u HANDLING\u NONE
避免移动项目,但我仍然得到不正确的处理<代码>间隔\u处理\u移动\u项目\u在\u跨度之间\u
可能是默认设置,无论如何,我的行为与未指定任何内容的情况相同。是的,我知道我只是说,在滚动适配器检查间隔后,然后将项目移动到正确的位置听起来是一个合理的解释。我明天去看看,谢谢!谷歌已经承认了这个错误:你的解决方案在这段时间内工作得很好,所以我接受这个建议。关于如何使跨度分配失效动画化,有什么想法吗?谷歌花了很长时间来解决这个问题。。。现在,这个布局管理器已经不可用了。@mato我不太清楚你所说的“设置跨度分配无效动画”是什么意思?我所看到的是,只要使用处理程序/后期解决方案(如上所述),就会发生适当的布局事件和动画。你是在问,当项目的跨度计数发生变化时,是否可以控制项目增长/收缩的持续时间?谢谢,但是谷歌已经在支持库22和更新版本中发布了修复程序。现在,它与我在原始问题中编写的代码一起开箱即用。
private class ScrollListener extends RecyclerView.OnScrollListener {
   @Override
   public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        gaggeredGridLayoutManager.invalidateSpanAssignments();
   }
}