Search code examples
androidandroid-recyclerviewlayoutparams

RecyclerView getLayoutParams returns null (sometimes)


I have a RecyclerView, for which I have a custom Adapter, and I have set the LayoutManager to be a FlexboxLayoutManager. For every child, I want to set FlexGrow to be 1.

In Google's example, demo-cat-gallery (https://github.com/google/flexbox-layout), they do this in the ViewHolder:

void bindTo(Drawable drawable) {
    mImageView.setImageDrawable(drawable);
    ViewGroup.LayoutParams lp = mImageView.getLayoutParams();
    if (lp instanceof FlexboxLayoutManager.LayoutParams) {
        FlexboxLayoutManager.LayoutParams flexboxLp = (FlexboxLayoutManager.LayoutParams) lp;
        flexboxLp.setFlexGrow(1.0f);
    }
}

This is then called by the RecyclerView.Adapter in onBindViewHolder. This works fine, but when I did the same in my app, it would only setFlexGrow for some items, and never the ones at the top. I realised that for some items (seemingly randomly), getLayoutParams() was returning null, but for others it was returning the correct FlexboxLayoutManager.LayoutParams.

I realised the difference was that in the Cat-gallery example, onCreateViewHolder was doing

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.viewholder_cat, parent, false);
    return new CatViewHolder(view);
}

while in my app I was doing

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
    return new MyViewHolder(new MyCustomView(getContext()));
}

It seems that inflating with a reference to the parent means that getLayoutParams() is never null, so changed my code to

View v = new MyCustomView(getContext());
parent.addView(v);
return new MyViewHolder(v);

And it now works correctly, with setFlexGrow() always being set. However, this feels wrong - I know you're not meant to explicitly add views to the RecyclerView parent.

So my question is:

1 - Why were LayoutParams randomly null for some items, but fine for others?

2 - How can I get LayoutParams to always be set, without doing something horrible like 'parent.addView(v);', or is it actually alright to do this?

Thanks :)


Solution

  • This appears to be a bug with FlexboxLayoutManager. I tried doing

    parent.addView(v);
    

    But this produced very strange crashes.

    It turns out that while FlexboxLayoutManager may not attach LayoutParams before onBindViewHolder, it will have done so before onViewAttachedToWindow. So you can setup the LayoutParams like:

    override fun onViewAttachedToWindow(holder: ViewHolder<View>) {
        super.onViewAttachedToWindow(holder)
        holder.itemView.updateLayoutParams {
            // Whatever
        }
    }
    

    OLD ANSWER: Still valid, but clumsier.

    In the end I produced this work-around:

    parent.addView(v);
    ViewGroup.LayoutParams params = v.getLayoutParams();
    parent.removeView(v);
    v.setLayoutParams(params);