【问题标题】:RecyclerView StaggeredGridLayoutManager reordering issueRecyclerView StaggeredGridLayoutManager 重新排序问题
【发布时间】:2015-01-06 14:17:25
【问题描述】:

我正在尝试在RecyclerViewStaggeredGridLayoutManager 中显示三个(至少我遇到问题的情况)项目和两列。第一项跨越两行。下面是它的样子:

现在,我将项目“项目 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();
    }
}

之后,数组就可以了:[Item 2, Item 1, Item 3]。但是,观点还差得很远:

如果我触摸RecyclerView(如果没有足够的项目可以滚动,足以触发过度滚动效果),第 2 项将向左移动,我希望首先看到它(带有漂亮的动画):

正如您可能在代码中看到的那样,我尝试通过调用 notifyDataSetChanged() 来替换 notifyItemMoved(idx, position)。它可以工作,但变化不是动画。

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

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


编辑:经过一番挖掘,没有必要使用完全跨度的项目来显示问题。我删除了全跨度。当我尝试将第 2 项移动到位置 0 时,它没有移动:第 1 项在它之后,第 3 项移动到右侧,所以我有:空单元格,第 2 项,新行,第 1 项,第 3 项。滚动后我的布局仍然正确。

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

【问题讨论】:

  • 我不知道这是否是一个错误,但我也看到过,只有当你滚动并选中 gap strategy 时,事情似乎才会移动
  • @tyczj: GAP_HANDLING_NONE 避开了移动的物品,但我仍然得到了不正确的处置。 GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS 可能是默认值,无论如何,我使用它的行为与不指定任何内容的行为相同。
  • 是的,我知道我只是说在您滚动适配器后检查间隙,然后将东西移动到正确的位置

标签: android android-recyclerview


【解决方案1】:

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

更新布局以使其看起来像您的第二个屏幕截图的技巧是在调用notifyItemMoved() 之后调用StaggeredGridLayoutManger (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);
}

在 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;

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

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

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

祝你好运!

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

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

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

【讨论】:

  • 听起来很合理的解释。我明天去看看,谢谢!
  • Google 已确认该错误:code.google.com/p/android/issues/detail?id=93711#c1您的解决方法在此期间工作正常,所以我会接受。
  • 关于如何为跨度分配失效设置动画的任何想法?
  • Google 花了很长时间来解决这个问题...到目前为止,这个布局管理器已经无法使用了。
  • @mato 我不确定你所说的“动画跨度分配失效”是什么意思?我所看到的是,只要使用处理程序/发布解决方法(上图),就会发生适当的布局事件和动画。您是在问是否可以控制项目在跨度计数发生变化时增长/收缩的持续时间?
【解决方案2】:

嗯,我已经这样做了。

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());

为自定义 RecyclerView 滚动监听器创建类

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

希望这会对你有所帮助。

【讨论】:

  • 谢谢,但 Google 已在支持库 22 和更新版本中发布了该修复程序。它现在可以使用我在原始问题中编写的代码开箱即用。
猜你喜欢
  • 2017-06-16
  • 2015-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-01
  • 2021-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多