user1010373
user1010373

Reputation: 289

Using custom view in RecyclerView widget

I'm having trouble rendering a custom view insider recycler view widget. Traditionally we inflate the view inside RecylerView.Adapter like

public RowLayoutViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    View view = LayoutInflater.from(context).inflate(R.layout.sample_view, viewGroup, false);
    RowLayoutViewHolder rowLayoutViewHolder = new RowLayoutViewHolder(view);
    return rowLayoutViewHolder;
}

This works fine and we can bind the data to the view inside onBindViewHolder(...) method. But when I try to create subclass of ViewGroup and use that like this, I get a blank ("black") screen.

public ImageGalleryAdapter.RowLayoutViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
  SampleView view = new SampleView(context);
  RowLayoutViewHolder rowLayoutViewHolder = new RowLayoutViewHolder(view);
  return rowLayoutViewHolder;
}

Here's my SampleView class -

public class SampleView extends ViewGroup {

  public SampleView(Context context) {
    super(context);
    initView();
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b)  {
    for (int i = 0; i < getChildCount(); i++) {
      View child = getChildAt(i);
      child.layout(l, t, l + 600, t + 200);
    }
  }

  private void initView() {
    inflate(getContext(), R.layout.sample_view, this);
    TextView textView = (TextView) findViewById(R.id.sampleTextView);
    textView.setTextColor(Color.WHITE);
    textView.setText("Hello from textview");
  }
}

And here is the layout -

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:background="@color/white"
          android:layout_width="match_parent"
          android:layout_height="wrap_content">

    <TextView
        android:id="@+id/sampleTextView"
        android:text="Sample TextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

Looking at the source code it seems like inflating the layout behaves exactly same as creating a view, except the inflation process adds a suitable LayoutParameter to the child view based on the rootView. I've tried adding that manually but without any result.

Any help will be appreciated.

Upvotes: 6

Views: 3428

Answers (4)

Froyo
Froyo

Reputation: 18477

It's pretty simple. If you see the way you add view by inflating it, you would realize that you are adding it to the parent (ViewGroup) but not attaching it. In this process, default LayoutParams are generated and set to your view. (Check LayoutInflater source)

SampleView view = new SampleView(context);
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

This is how it should be. Even the following seems to be working.

viewGroup.add(view, new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

Upvotes: 5

rahul.taicho
rahul.taicho

Reputation: 1379

In your onCreateViewHolder() try setting the params to the view you've created and for width set it to parent.getMeasuredWidth() see if that helps, it solved my problem.

Upvotes: 0

AllDayAmazing
AllDayAmazing

Reputation: 2383

I've successfully solved the issue in a roundabout way that might help

In the custom View, say SampleView, in whatever method you wish to modify the layout (I guess here onLayout()), register

getViewTreeObserver().addOnGlobalLayoutListener(new LayoutViewTreeObserver())

in the callback for LayoutViewTreeObserver:

@Override
    public void onGlobalLayout() {
        // Do layout stuff here
    }

Also, as a side note, don't forget to remove the GlobalOnLayoutListener before the modifications so the callback doesn't get anymore necessary calls (in practice, I've had to call getViewTreeObserver() instead of keeping a reference to the observer as a lot of times a reference stops being "alive" and throws an error when I try to unregister it).


While the above worked for me, I realized that it is far from ideal since it relies on the fact that the custom view generally obtain the LayoutParams of the container class, which if used elsewhere will not be RecyclerView.LayoutParams, thus causing an error. However, I have found that using an XML layout with just the CustomView as a base tag in the XML and inflating/binding the standard way works as expected. It has something to do with how RecyclerView attached the view and the measurement(s) done before, but I have yet to do a deep dive to discover why exactly.

Upvotes: 1

NewUser
NewUser

Reputation: 156

I'm used to the initial methodology, so I'm not sure about what's happening. have you tried calling initView from onLayout() method?

Upvotes: 0

Related Questions