强制RecyclerView调用onCreateViewHolder方法

11

我有一个RecyclerView,可以在运行时以列表、小网格或大网格的形式显示项目。根据用户选择的样式,我在onCreateViewHolder中填充不同的布局。

我还使用layoutManger.setSpanSizeLookUp()来在不同的样式之间切换。我的代码如下所示:

layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
        @Override
        public int getSpanSize(int position) {
            if(showType == ProductAdapter.SHOW_TYPE_SMALL_GRID)
                return 1;
            else
                return columnCount; //show one item per row
        }
    });

@Override
public void onClick(View v) {
    if(showType == ProductAdapter.SHOW_TYPE_SMALL_GRID)
        showType = ProductAdapter.SHOW_TYPE_LARGE_GRID;
    else
        showType = ProductAdapter.SHOW_TYPE_SMALL_GRID;


    int firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
    adapter = new ProductAdapter(getActivity(), productList, showType);
    recyclerView.setAdapter(adapter);
    layoutManager.scrollToPosition(firstVisibleItem);
}

问题是我要强制调用onCreateViewHolder,每次用户更改样式时都会创建一个新对象。还有其他方法吗?!强制调用onBindViewHolder()以便重新调用。我只是使用adapter.notifyDataSetChanged()。如何获得类似于onCreateViewHolder的东西?

任何不使用多个适配器的解决方案都足够好!


很好,你说“不使用多个适配器”是正确的。替换适配器并不是一个好的解决方案。 - Bugs Happen
2个回答

19

你需要做的是:

  1. 修改你的 Adapter

  • 指定两种 Views 类型,以便你的 Adapter 可以填充它们:

private static final int LARGE_GRID_ITEM = -1;
private static final int SMALL_GRID_ITEM = -2;
  • 创建一个可以存储当前类型的字段 mCurrentType

  • 使用你的 AdaptergetItemViewType 方法。例如:

@Override
public int getItemViewType (int position) {
    return mCurrentType;
}
  • 在您的createViewHolder方法中使用viewType来决定需要创建哪种类型的ViewHolder

public final RecyclerView.ViewHolder createViewHolder (ViewGroup parent, int viewType){
    if (viewType == LARGE_GRID_ITEM) {
        //return large grid view holder
    } else {
        //return small grid view holder
    }
}
  • 此外,您可以创建方法:

public void toggleItemViewType () {
    if (mCurrentType == LARGE_GRID_ITEM){
        mCurrentType = SMALL_GRID_ITEM;
    } else {
        mCurrentType = LARGE_GRID_ITEM;
    }
}

public boolean displaysLargeGrid(){
    return mCurrentType == LARGE_GRID_ITEM;
}
  1. 修改您发布的代码:

layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
        @Override
        public int getSpanSize(int position) {
            if (adapter.displaysLargeGrid()) {
                return 1;
            } else {
                return columnCount; //show one item per row
            }
        }
    });

@Override
public void onClick(View v) {
    adapter.toggleItemViewType();
    adapter.notifyDataSetChanged();
}

3
我认为你没有理解我的问题。你的代码没问题,但当调用toggle时,mCurrentType会发生变化,但createViewHolder不会被调用!notifyDataSetChanged()只会强制调用onBindView而不是onCreateViewHolder()。所以问题仍然存在,如何强制调用onCreateViewHolder()以重新填充正确的项目? - Alireza Ahmadi
1
我理解了你的问题,但你没有理解我的答案。它会针对你的问题提供适当的方法。你试过我的代码吗?如果大网格没有持有者,它将调用onCreateViewHolder。我的代码更改了getItemViewType返回的内容。当您调用notifyDataSetChanged时,适配器将检查位置的itemViewTypes,并决定是否需要调用onCreateViewHolder(如果没有此类型的视图的可用持有者)或仅调用onBindViewHolder(如果已经有一些剩余的持有者)。您需要拥有两种类型的持有者。 - Bartek Lipinski
1
他的代码可以工作,因为他通过调用notifyDataSetChanged()来清除了整个列表,但这对性能不利。你应该调用notifyItemChanged()。调用notifyDataSetChanged()就像重新创建整个列表一样。对于小列表,你可以这样做,但是对于大量项目或者单行布局较重的情况下,你不应该这样做。 - Mayank
这不是一个答案!你完全改变了标题。 - mahdi azarm
@mahdiazarm SO的问题不仅仅是一个标题,它还有描述。这是针对问题的正确答案。 - Bartek Lipinski

3

不是最佳选择,但最好创建一个新的Adapter,它将调用onCreateViewHolder()方法。这样可以避免问题出现,但会带来微小的性能问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接