我遇到一个简单的多列布局RecyclerView的问题,它在平板电脑上可以最好地重现。我已经创建了一个带有完整功能源代码的基本示例,可能更容易快速运行它,而不是试图理解我的意思;)
平板电脑(我有网格布局)的主要问题是,当隐藏特定项目并再次显示它们时,项目会被奇怪地重新排列(通过notifyItemRemoved()和notifyItemInserted())。我认为这甚至可以通过删除和插入第一项来重现。布局管理器在顶部插入一个额外的行,并从下面的第一行移动项目以填充它。 (以下示例删除并插入每个第三项,从第一项开始)
我有不同类型的项目(例如红色,绿色和蓝色项目),我想切换特定类型的项目(在示例中,您可以通过单击FloatingActionButton来切换红色项目。)
我想要修复的奇怪行为可以通过单击FAB两次而无需滚动来重现。这将首先过滤红色项目,然后再次显示它们。第二次切换过滤器时,您会注意到上面插入了一行项目,第一行中的项目被移动以填充它。 如果您在动画期间没有注意到,只需在切换过滤器后向上滚动。
我希望第一行保持原样,只有红色项目再次显示。
有人知道如何解决这个问题吗?
以下是活动代码:
File.Delete
这是布局代码:
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);
}
}
}
}
答案 0 :(得分:0)
首先,我应该提到问题不在DefaultItemAnimator
。如果将动画设置为null
,则会有相同的行为。实际上根本没有问题。行为是预期的,因为您只是将项目插入TestAdapter
的第一个位置,然后其他项目正在转移,但您的RecyclerView
保持在同一位置。
所以您只需在recyclerView.scrollToPosition(0);
方法的末尾添加toggleFiltered()
行,这样一旦您应用了过滤,它就会向上滚动。它会按你的意愿工作。
答案 1 :(得分:0)