回收站空间的ItemDecoration在滚动期间不断增加

3
这是我的第一个stackoverflow问题。在过去的几个小时里,我一直在stackoverflow上寻找解决这个问题的方法,但我找不到任何答案。
我创建的应用程序(或特定活动)非常简单,基本上是一个包含TextView和RecyclerView的垂直滚动视图,类似于:
垂直RecyclerView --- TextView(标题) --- 水平RecyclerView(卡片视图) --- TextView(标题) --- 水平RecyclerView(卡片视图) ...(更多内容在这里) --- TextView(标题) --- 水平RecyclerView(卡片视图) (垂直RecyclerView结束)
除了当我开始滚动垂直滚动视图(向上和向下)时,第一个和最后两个水平RecyclerView的空格项装饰会持续增加之外,一切都正常工作。我猜测问题可能出在哪里,但我不确定如何解决。
水平RecyclerView适配器(非常标准)
 public class HorizontalRecyclerViewAdapter extends RecyclerView.Adapter<HorizontalRecyclerViewAdapter.ViewHolder> {
private ArrayList<String> mDataset;
private Context mContext;

// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class ViewHolder extends RecyclerView.ViewHolder {
    // each data item is just a string in this case
    public TextView txtViewTitle;
    public ImageView imgViewIcon;

    public ViewHolder(View itemLayoutView) {
        super(itemLayoutView);
        txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.text_view);
        imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.image_view);
    }
}

// Provide a suitable constructor (depends on the kind of mDataset)
public HorizontalRecyclerViewAdapter(Context myContext, ArrayList myDataset) {
    this.mContext = myContext;
    this.mDataset = myDataset;
}

// Create new views (invoked by the layout manager)
@Override
public HorizontalRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                                   int viewType) {
    // create a new view
    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.view_image, parent, false);
    // Goes here: set the view's size, margins, paddings and layout parameters

    ViewHolder viewHolder = new ViewHolder(v);
    return viewHolder;
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    // - get element from your mDataset at this position
    // - replace the contents of the view with that element

    String mDrawableName = mDataset.get(position);
    int resID = mContext.getResources().getIdentifier(mDrawableName , "drawable", mContext.getPackageName());
    holder.imgViewIcon.setImageResource(resID);
}

// Return the size of your mDataset (invoked by the layout manager)
@Override
public int getItemCount() {
    return mDataset.size();
}

空间条目装饰
class HorizantalSpaceItemDecoration extends RecyclerView.ItemDecoration {

private final int mHorizantalSpaceWidth;

public HorizantalSpaceItemDecoration(int horizantalItemSpace) {
    mHorizantalSpaceWidth = horizantalItemSpace;
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                           RecyclerView.State state) {
    outRect.right = mHorizantalSpaceWidth;
}

垂直循环适配器:

public class VerticalRecyclerViewAdapter extends RecyclerView.Adapter<VerticalRecyclerViewAdapter.ViewHolder> {

private ArrayList<String> mHeaderList;
private ArrayList<String> mSportList;
private ArrayList<Boolean> mSpaceItemDecorationFlagList;
private Context context;

public static final int HORIZANTAL_ITEM_SPACE = 30;

// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class ViewHolder extends RecyclerView.ViewHolder {
    // each data item is just a string in this case
    public TextView textView;
    public CardView cardView;
    public RecyclerView recyclerView;

    public ViewHolder(View itemLayoutView) {
        super(itemLayoutView);
        cardView = (CardView) itemLayoutView.findViewById(R.id.card_view);
        textView = (TextView) itemLayoutView.findViewById(R.id.text_view);
        recyclerView = (RecyclerView) itemLayoutView.findViewById(R.id.recycler_view);
    }
}

// Provide a suitable constructor (depends on the kind of mHeaderList)
public VerticalRecyclerViewAdapter(Context context) {
    Resources res = context.getResources();
    String[] headers = res.getStringArray(R.array.header_home);
    mHeaderList = new ArrayList<String>(Arrays.asList(headers));
    String[] sports = res.getStringArray(R.array.sports);
    mSportList = new ArrayList<String>(Arrays.asList(sports));
    mSpaceItemDecorationFlagList = new ArrayList<Boolean>();
    for(int i = 0; i < mHeaderList.size(); i++){
        mSpaceItemDecorationFlagList.add(false);
    }
    this.context = context;
}

@Override
public int getItemViewType(int position) {
    // Just as an example, return 0 or 2 depending on position
    // Note that unlike in ListView adapters, types don't have to be contiguous
    return position % 2 * 2;
}

// Create new views (invoked by the layout manager)
@Override
public VerticalRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                                 int viewType) {
    // Create a new view depends on view type
    // 0: Text View (Header)
    // 1: Recycler View (Category Explorer)
    View newView;

    if(viewType == 0) {
        newView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.view_text, parent, false);
    }else{
        newView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.view_recycler, parent, false);
    }
    // Goes here: set the view's size, margins, paddings and layout parameters

    ViewHolder viewHolder = new ViewHolder(newView);
    return viewHolder;
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    // - get element from your mHeaderList at this position
    // - replace the contents of the view with that element
    if(position % 2 == 0)
    {
        String header = mHeaderList.get(position);
        holder.textView.setText(header);
    }else{
        // ??????????????
        // Problem might be holder.recyclerView not reset properly?

        RecyclerView.LayoutManager popularLayoutManager= new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
        RecyclerView.Adapter adapter = new HorizontalRecyclerViewAdapter(context, mSportList);
        holder.recyclerView.setLayoutManager(popularLayoutManager);
        holder.recyclerView.setAdapter(adapter);

        //add Space ItemDecoration
        holder.recyclerView.addItemDecoration(new HorizantalSpaceItemDecoration(HORIZANTAL_ITEM_SPACE));
        holder.recyclerView.setHasFixedSize(true);

    }
}

// Return the size of your mHeaderList (invoked by the layout manager)
@Override
public int getItemCount() {
    return mHeaderList.size();
}

在每个水平滚动视图中,我试图在每个图像/项目之间添加30 dp空间,在滚动期间,第一个水平和最后两个水平滚动视图保持增加(我猜是30,然后60,然后90?)。
我的猜测是在onBindViewHolder()中,我的holder的滚动视图没有正确重置,它的rect.right不断更新为新值(旧值+30)。但这仅仅是我的猜测。请问还有其他人可以帮忙吗?
提前致谢。

当您到达最后一个项目时,您正在添加一个itemdecorator,并同时滚动,这使得holder尝试绑定另一个元素,可能是另一个itemDecoration。请检查您的onBindViewHolder()方法是否需要另一个条件。 - Jonathan Aste
2个回答

8

如果您遇到此问题,可能是因为每次更新数据时都添加了item decoration视图(这很糟糕)。但是作为快速解决方法,您可以检查recycler view是否添加了任何item decorators,如果没有,则可以添加您自己的。将此添加到初始化适配器的Activity或fragment文件中。

if (your_recycler_view.itemDecorationCount < 1) {
    your_recycler_view.addItemDecoration(HorizantalSpaceItemDecoration())
}

非常感谢,我之前也遇到了水平RecyclerView的类似问题,现在通过您的解决方案已经解决了。 - Steve

6
这里的问题是项目装饰器在每次将数据绑定到视图持有者时都会被添加,而之前添加的则永远不会被删除。可以进行检查来防止这种情况发生,或者我们可以删除已有的装饰器。但更好的方法是仅在创建视图持有者时声明和添加装饰器。因此,为了解决这个问题:
在类VerticalRecyclerViewAdapter中:
1.当前正在onBindViewHolder方法中添加item装饰器。该方法在重新渲染持有者时(滚动时)每次调用,因此我们需要从这里删除它。
2.然后进入onCreateViewHolder方法,在那里向viewholder添加装饰。
3.请注意,在您的情况下需要检查viewType == 1,否则会导致NullPointer。
因此,您的onCreateViewHolder方法可如下所示:
// Create new views (invoked by the layout manager)
@Override
public VerticalRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                                 int viewType) {
    // Create a new view depends on view type
    // 0: Text View (Header)
    // 1: Recycler View (Category Explorer)
    View newView;

    if (viewType == 0) {
        newView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.view_text, parent, false);
    } else {
        newView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.view_recycler, parent, false);
    }
    // Goes here: set the view's size, margins, paddings and layout parameters

    ViewHolder viewHolder = new ViewHolder(newView);
    if (viewType == 1) {
        // Adding the itemDecorator here
        viewHolder.recyclerView.addItemDecoration(new HorizantalSpaceItemDecoration(HORIZANTAL_ITEM_SPACE));
    }
    return viewHolder;
}

谢谢,解答了我的问题。非常有帮助。 - user8048493
欢迎您,如果这个回答完全解决了问题,请将其标记为解决方案 @user8048493 - Alex
谢谢,它也帮助了我。 - Rohit Singh

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