Using custom view in RecyclerView widget
I am having trouble rendering a widget widget for widgets. Traditionally we inflate the view inside the 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 great and we can bind data to the view inside the onBindViewHolder (...) method. But when I try to subclass ViewGroup and use it 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 that inflating the layout behaves exactly like creating a view, except that the inflation process adds a suitable LayoutParameter to the rootView based child view. I tried adding this manually but without any results.
Any help would be appreciated.
source to share
It's pretty simple. If you see a way to add a view by inflating it, you know you are adding it to the parent (ViewGroup), but not attaching it. This process creates default LayoutParams by default. (Check source LayoutInflater)
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 work.
viewGroup.add(view, new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
source to share
I have successfully solved the problem with a workaround that might help
In a custom view, say SampleView, in any method you want to change the layout (I think 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, remember to remove the GlobalOnLayoutListener before making changes, so the callback doesn't receive any more calls it needs (in practice I had to call getViewTreeObserver()
instead of referring to the observer how many times the link stops being "live" and throws an error. when I try to cancel it).
While the above worked for me, I realized that it is far from ideal as it relies on the fact that the custom view usually gets the LayoutParams of the container class, which, if used elsewhere, will not be RecyclerView.LayoutParams, which is causes an error. However, I found that using the XML layout only with the CustomView as the base tag in the XML and bloating / binding the standard way works as expected. It has something to do with how the RecyclerView bind the view and dimensions (dimensions) before, but I still have a deep dive to do to figure out why.
source to share