Android RecyclerView DefaultItemAnimator-项目移动异常
我有一个问题,一个简单的多柱布局回收视图,这是最好的再现片。我在下面创建了一个具有完整功能的源代码的基本示例,也许它更容易快速运行,而不是试图理解我的意思;) 平板电脑(我有一个网格布局)的主要问题是,当隐藏特定项目并再次显示它们时(通过notifyItemRemoved()和notifyItemInserted()),项目会被奇怪地重新排列。我认为这甚至可以复制删除和插入只有第一项。布局管理器在顶部插入一个额外的行,并从下面的第一行移动项目以填充该行。(以下示例从第一项开始,每隔三项删除和插入一项) 我有不同类型的项目(在示例中为红色、绿色和蓝色项目),我希望切换特定类型的项目(在示例中,单击FloatingAction按钮可以切换红色项目) 我想修复的奇怪行为可以通过点击两次晶圆厂而不在之前滚动来复制。这将首先过滤红色项目,然后再次显示它们。第二次切换过滤器时,您会注意到上面插入了一行项目,并移动第一行中的项目以填充它。 如果在动画中没有注意到,只需在切换过滤器后向上滚动即可 我希望第一行保持原样,只显示红色项目 有人知道怎么解决吗 以下是活动代码:Android RecyclerView DefaultItemAnimator-项目移动异常,android,android-layout,animation,android-recyclerview,Android,Android Layout,Animation,Android Recyclerview,我有一个问题,一个简单的多柱布局回收视图,这是最好的再现片。我在下面创建了一个具有完整功能的源代码的基本示例,也许它更容易快速运行,而不是试图理解我的意思;) 平板电脑(我有一个网格布局)的主要问题是,当隐藏特定项目并再次显示它们时(通过notifyItemRemoved()和notifyItemInserted()),项目会被奇怪地重新排列。我认为这甚至可以复制删除和插入只有第一项。布局管理器在顶部插入一个额外的行,并从下面的第一行移动项目以填充该行。(以下示例从第一项开始,每隔三项删除和插入
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private TestAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
((FloatingActionButton) findViewById(R.id.fab)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
toggleFiltered();
}
});
final int columnCount = getColumnCount(300);
GridLayoutManager layoutManager = new GridLayoutManager(this, columnCount);
recyclerView.setLayoutManager(layoutManager);
adapter = new TestAdapter();
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
}
private void toggleFiltered() {
final boolean filtered = adapter.isFiltered();
adapter.setFiltered(!filtered);
if(filtered) {
adapter.notifyItemInserted(0);
adapter.notifyItemInserted(3);
adapter.notifyItemInserted(6);
adapter.notifyItemInserted(9);
adapter.notifyItemInserted(12);
} else {
adapter.notifyItemRemoved(0);
adapter.notifyItemRemoved(3);
adapter.notifyItemRemoved(6);
adapter.notifyItemRemoved(9);
adapter.notifyItemRemoved(12);
}
}
int getColumnCount(final int dpThreshold){
final Display display = getWindowManager().getDefaultDisplay();
final DisplayMetrics outMetrics = new DisplayMetrics();
display.getMetrics(outMetrics);
final float density = getResources().getDisplayMetrics().density;
final float dpWidth = outMetrics.widthPixels / density;
return ((int) dpWidth) / dpThreshold;
}
class TestAdapter extends RecyclerView.Adapter {
public static final int TYPE_RED = 0;
public static final int TYPE_GREEN = 1;
public static final int TYPE_BLUE = 2;
private boolean filtered = false;
public boolean isFiltered() {
return filtered;
}
public void setFiltered(final boolean filtered) {
this.filtered = filtered;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
final View view = new View(parent.getContext());
view.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 500));
int color = Color.RED;
if(viewType == TYPE_GREEN){
color = Color.GREEN;
} else if (viewType == TYPE_BLUE){
color = Color.BLUE;
}
return new TestViewHolder(view, color);
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
}
@Override
public int getItemViewType(final int position) {
if(!filtered) {
if ((position + 3) % 3 == 0) {
return TYPE_RED;
}
if ((position + 2) % 3 == 0) {
return TYPE_GREEN;
}
} else {
if ((position) % 2 == 0) {
return TYPE_GREEN;
}
}
return TYPE_BLUE;
}
@Override
public int getItemCount() {
if(filtered) {
return 10;
}
return 15;
}
class TestViewHolder extends RecyclerView.ViewHolder {
public TestViewHolder(final View itemView, final int color) {
super(itemView);
itemView.setBackgroundColor(color);
}
}
}
}
这是布局代码:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@+id/root_layout"
app:layout_constraintLeft_toLeftOf="@+id/root_layout"
app:layout_constraintRight_toRightOf="@+id/root_layout"
app:layout_constraintTop_toTopOf="@+id/root_layout"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/root_layout"
app:layout_constraintRight_toRightOf="@+id/root_layout"
android:layout_margin="16dp"
/>
</android.support.constraint.ConstraintLayout>
首先,我应该指出问题不在
DefaultItemAnimator
中。如果将动画师设置为null
,则会有相同的行为。实际上根本没有问题。该行为是预期的,因为您只是将项目插入到测试适配器的第一个位置,然后其他项目正在移动,但您的回收视图
仍保持在相同的位置
所以您只需添加recyclerView.scrollToPosition(0)在toggleFiltered()
方法末尾的code>行,因此一旦应用了过滤,它将向上滚动。它将按照您的意愿工作。首先,我应该指出问题不在DefaultItemAnimator
中。如果将动画师设置为null
,则会有相同的行为。实际上根本没有问题。该行为是预期的,因为您只是将项目插入到测试适配器的第一个位置,然后其他项目正在移动,但您的回收视图
仍保持在相同的位置
所以您只需添加recyclerView.scrollToPosition(0)在toggleFiltered()
方法末尾的code>行,因此一旦应用了过滤,它将向上滚动。它会按照你的意愿工作。这并不奇怪。因为您插入了NOTIFYTEMINSERTED(0)。它是插入项。未通知DataSetChanged()。适配器正在显示项目“继续”。它是绿色的。您将notifyItemInserted(0)插入红色,但显示第一个项目的适配器仍为绿色(它是1)。因为adapter notifyItemInserted(0)在显示第一个项目之前已显示为绿色。这并不奇怪。因为您插入了NOTIFYTEMINSERTED(0)。它是插入项。未通知DataSetChanged()。适配器正在显示项目“继续”。它是绿色的。您将notifyItemInserted(0)插入红色,但显示第一个项目的适配器仍为绿色(它是1)。因为adapter notifyItemInserted(0)在显示第一个项目之前已显示为绿色。嘿,rom4ek,你说得对,问题不在DefaultItemAnimator中,它似乎来自负责定位的LayoutManager。。。如果我将LayoutManager设置为null,那么我可以应用您的修复。但是我希望动画发生,动画的行为只是让人困惑,我认为这根本不是我所期望的。嗯,我想你没有得到我的答案-没有必要将defaultitemaniator
或LayoutManager
设置为null。您只需添加recyclerView.scrollToPosition(0)在您的toggleFiltered()
方法的末尾,使用code>,它将起作用。好的,您是对的,滚动到位置0确实解决了问题,即使我不知道原因;)在我真正的应用程序中,当列表没有滚动到顶部时,我仍然存在一些问题,但也许我必须找到一种通用方法来计算我必须滚动到的位置。你的意思是recyclerView.scrollToPosition(0)代码>在您的真实应用程序中不起作用?如果是这样的话,您可以在延迟一段时间后尝试这样做,比如recyclerView.postDelayed(()->recyclerView.scrollToPosition(0),50)
,以确保已经执行了筛选。不,scrollToPosition(0)可以工作,但我并不总是想滚动到顶部。当用户已经向下滚动时,仍然存在一些问题,但您的回答肯定是朝着正确方向迈出的一步,因此我将奖励您奖金;)嘿rom4ek,你说得对,问题不在DefaultItemAnimator中,它似乎来自负责定位的LayoutManager。。。如果我将LayoutManager设置为null,那么我可以应用您的修复。但是我希望动画发生,动画的行为只是让人困惑,我认为这根本不是我所期望的。嗯,我想你没有得到我的答案-没有必要将defaultitemaniator
或LayoutManager
设置为null。您只需添加recyclerView.scrollToPosition(0)在您的toggleFiltered()
方法的末尾,使用code>,它将起作用。好的,您是对的,滚动到位置0确实解决了问题,即使我不知道原因;)在我真正的应用程序中,当列表没有滚动到顶部时,我仍然有一些问题,但是