I'm trying to use androids databinding feature in new project and so far very happy with it.
But now i came across a problem in my recyclerviews viewholder.
My viewholder uses different layouts (based on the viewtype when it gets created)
public MediaViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
switch(viewType){
case HEADER:
int layout = R.layout.item_media_header;
break;
case DEFAULT:
int layout = R.layout.item_media_default;
break;
case SMALL:
int layout = R.layout.item_media_small;
break;
}
View v = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
return new MediaViewHolder(v);
}
So all of those 3 layouts have the same views in it, only arranged differently. So the binding of the model to the views is the same.
Anyways, based on those layouts android creates
- ItemMediaHeaderBinding
- ItemMediaDefaultBinding
- ItemMediaSmallBinding
Which sucks since it would force me to create 3 different ViewHolder classes or instantiate the right binding Class by checking which layout is used.
Is there a best practice for this case? Is there a possibility to simply create a superclass for those three binding classes like "ItemMediaBinding".
Thanks in advance.
So, what I ended up doing, was to create a BaseViewHolder.class
, create other ViewHolder
classes that extend this BaseViewHolder
, so that my RecyclerView.Adapter
still looks clean, because I ended up with something like this as a ViewHolder
:
public class BaseVH extends RecyclerView.ViewHolder {
private ViewDataBinding mBinding;
public BaseVH(ViewDataBinding mBinding) {
super(mBinding.getRoot());
this.mBinding = mBinding;
}
public void displayPost(final NewsItem post) {
PostItemViewModel vm = new PostItemViewModel();
vm.getPost().set(post);
mBinding.setVariable(BR.viewModel, vm);
}
}
public class TextPostVH extends BaseVH {
private final PostTextBinding mBinding;
public TextPostVH(final PostTextBinding binding) {
super(binding);
mBinding = binding;
}
//Have a method like this in the BaseVH.class
@Override
public void displayPost(final NewsItem post) {
if (mBinding.getViewModel() == null) {
mBinding.setViewModel(new PostItemViewModel());
}
mBinding.getViewModel().getPost().set(post);
}
}
So you can simply call in the onBindViewHolder
:
@Override
public void onBindViewHolder(final BaseVH holder, final int position) {
//only a ViewHolder containing ads has to be filtered, the others work with displayPost()
if (holder instanceof AdVH){
holder.displayPost(mPosts.get(position));
((AdVH) holder).displayAd();
} else {
holder.displayPost(mPosts.get(position));
}
}
You can specify the Binding class names
in your layouts
in the data
-tag:
<data class="PostTextBinding" />
But I don't think that you are allowed to use the same class names in different layouts. You can find my (sample) project at GitHub.
Another solution is explained here on Github. They have created a (really) universal RecyclerView.Adapter
, but I found this... a little too much for me at this moment.
@Amylinn is right, that really do for you, and you can make it even much easy:
// Only one ViewHolder at all
public class BaseVH extends RecyclerView.ViewHolder {
private ViewDataBinding mBinding;
public BaseVH(ViewDataBinding mBinding) {
super(mBinding.getRoot());
this.mBinding = mBinding;
}
public ViewDataBinding() {
return mBinding;
}
}
In the onBindViewHolder
:
@Override
public void onBindViewHolder(final BaseVH holder, final int position) {
// Just use "model" as name in any layouts
holder.getBunding().setVariable(BR.model, mPosts.get(position));
}
But you still has to create view holder with different layout in onCreateViewHolder
Or if you like using delegate, you can look at this approach - https://github.com/drstranges/DataBinding_For_RecyclerView