RecyclerView StaggeredGridLayoutManager 重新排序问题
RecyclerView StaggeredGridLayoutManager reordering issue
我正在尝试在 RecyclerView
和 StaggeredGridLayoutManager
两列中显示三个项目(至少我有问题)。第一项跨越两行。这是它的样子:
现在,我要将项目 "Item 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()
因为我想保持动画的一致性。
编辑:经过一些挖掘,不需要 fully-spanned 项来显示问题。我删除了 full-span。当我尝试将项目 2 移动到位置 0 时,它 没有移动 :项目 1 在它后面移动,项目 3 向右移动,所以我有:空单元格,项目2,换行,Item 1,Item 3。滚动后我的布局还是正确的。
更有趣的是,我没有 GridLayoutManager
的问题。我需要一个 full-span 项目,所以它不是解决方案,但我想这确实是 StaggeredGridLayoutManager
…
中的一个错误
我没有完整的答案,但我可以向您指出解决方法和错误报告(我认为它们是相关的)。
更新布局使其看起来像您的第二个屏幕截图的技巧是在调用 notifyItemMoved()
之后在 StaggeredGridLayoutManger
(sglm) 上调用 invalidateSpanAssignments()
。 "challenge"就是在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;
结果是您的动画仍然 运行s,布局以您想要的方式结束。这是一个真正的kludge,但它确实有效。我相信这与我在以下 link:
中发现和报告的错误相同
https://code.google.com/p/android/issues/detail?id=93156
虽然我的 "symptom" 和要求的调用不同,但根本问题似乎是相同的。
祝你好运!
编辑:无需post延迟,只需posting 即可解决问题:
case R.id.move_sec_top:
adapter.moveItem(2, 0);
new Handler().post(new Runnable() {
@Override
public void run() {
sglm.invalidateSpanAssignments();
}
});
return true;
我最初的理论是调用被阻塞直到布局传递结束,但我相信事实并非如此。相反,我现在认为,如果您立即调用 invalidateSpanAssignments()
,它实际上执行得太早了(在布局更改完成之前)。因此,上面的 post(没有延迟)只是将调用添加到布局之后发生的渲染队列的末尾。
好吧,我已经这样做了。
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 滚动监听器 创建 class。
private class ScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
gaggeredGridLayoutManager.invalidateSpanAssignments();
}
}
希望对您有所帮助。
我正在尝试在 RecyclerView
和 StaggeredGridLayoutManager
两列中显示三个项目(至少我有问题)。第一项跨越两行。这是它的样子:
现在,我要将项目 "Item 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()
因为我想保持动画的一致性。
编辑:经过一些挖掘,不需要 fully-spanned 项来显示问题。我删除了 full-span。当我尝试将项目 2 移动到位置 0 时,它 没有移动 :项目 1 在它后面移动,项目 3 向右移动,所以我有:空单元格,项目2,换行,Item 1,Item 3。滚动后我的布局还是正确的。
更有趣的是,我没有 GridLayoutManager
的问题。我需要一个 full-span 项目,所以它不是解决方案,但我想这确实是 StaggeredGridLayoutManager
…
我没有完整的答案,但我可以向您指出解决方法和错误报告(我认为它们是相关的)。
更新布局使其看起来像您的第二个屏幕截图的技巧是在调用 notifyItemMoved()
之后在 StaggeredGridLayoutManger
(sglm) 上调用 invalidateSpanAssignments()
。 "challenge"就是在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;
结果是您的动画仍然 运行s,布局以您想要的方式结束。这是一个真正的kludge,但它确实有效。我相信这与我在以下 link:
中发现和报告的错误相同https://code.google.com/p/android/issues/detail?id=93156
虽然我的 "symptom" 和要求的调用不同,但根本问题似乎是相同的。
祝你好运!
编辑:无需post延迟,只需posting 即可解决问题:
case R.id.move_sec_top:
adapter.moveItem(2, 0);
new Handler().post(new Runnable() {
@Override
public void run() {
sglm.invalidateSpanAssignments();
}
});
return true;
我最初的理论是调用被阻塞直到布局传递结束,但我相信事实并非如此。相反,我现在认为,如果您立即调用 invalidateSpanAssignments()
,它实际上执行得太早了(在布局更改完成之前)。因此,上面的 post(没有延迟)只是将调用添加到布局之后发生的渲染队列的末尾。
好吧,我已经这样做了。
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 滚动监听器 创建 class。
private class ScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
gaggeredGridLayoutManager.invalidateSpanAssignments();
}
}
希望对您有所帮助。