Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android DataBinding in RecyclerView.ViewHolders with different layouts

Tags:

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.

like image 292
hightower Avatar asked Jul 02 '16 15:07

hightower


2 Answers

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.

like image 83
yennsarah Avatar answered Sep 28 '22 04:09

yennsarah


@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

like image 29
Roman_D Avatar answered Sep 28 '22 04:09

Roman_D