Generate your own RecyclerView.Adapter with blackjack and DataBinding

Aren’t you tired of creating RecyclerView.Adapters over and over again? It would seem that with DataBinding coming we’ll “write more code by writing less code”, but this Adapters “generating” seems like to be endless. So, why wouldn’t you generate your adapters with few annotations and even less lines of code?

How did I handle all this stuff before

Until now one of my best approach was this:

public class BindingViewHolder<T extends ViewDataBinding> extends     RecyclerView.ViewHolder { 

    private T binding; 

    public BindingViewHolder(T binding){ 
       super(binding.getRoot()); 
       this.binding = binding; 
    } 

    public T getBinding() { return binding; }
}

With this you basically can pass any DataBingingUtil.inflate(..) into this ViewHolder constructor and you’ll receive your fancy ViewHolder with DataBinding abilities.

What’s problem with this? Yes, really, you don’t need to create bunch of ViewHolders, but you still need to create bunch of Adapters, with onCreateViewHolder , onBindViewHolder and so on. And this is dramatic waste of time.

When all started

From past Google I/O I noticed one thing:

We love Annotations

So I thought, if Java and Android in part have power of annotation processing, why can’t I make something generate this endless code for me?

The solution

And with some time passed I came to this

@Adapter( 
    dataSetType = @DataSetType(dataSetType = TestData.class),       
    useViews = { @ViewFor(dataClass = TestData.class, viewRes = R.layout.rv_item_test_adapter) }, 
    map = {
        @Bind(bindClass = TestData.class, bindMode = BindMode.variable, bindingResourceName = “testData”), 
        @Bind(bindClass = Void.class, bindMode = BindMode.method, bindingResourceName = “clickAdapter”, methodName = “myMethod”, methodCallMode = MethodCallMode.onClick)
    }
)
public class TestAdapter { }

Isn’t it smaller than one that you’r writing each time? Definitely smaller that mine ones.

“But this is not RecyclerView.Adapter ” you’ll say. You’ll be right, but created TestAdapterGenerated definitely will.

What is it? I’ll show you.

public abstract class TestAdapterGenerated extends Adapter<BindingViewHolder> implements DataSetInterface {
    List<TestData> data;

    {
        data = new java.util.ArrayList<>();
    }

    @Override
    @CallSuper
    public int getItemViewType(int position) {
        return 
ViewTypeEnum.getViewTypeFromDataClass(data.get(position).getClass());
    }

    @Override
    @CallSuper
    public BindingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new com.ekalips.BindingViewHolder<>(ViewTypeEnum.getLayoutIdFromViewType(viewType), parent);
    }

    @Override
    @CallSuper
    public void onBindViewHolder(final BindingViewHolder holder, int position) {
        final Object currentData = data.get(holder.getAdapterPosition());
        if (currentData instanceof com.ekalips.annotationbindingrecyclerview.data.TestData) {
     holder.getBinding().setVariable(com.ekalips.AnnotationBRMapper.getBRForName("testData"), currentData);
    }
    holder.getBinding().setVariable(com.ekalips.AnnotationBRMapper.getBRForName("clickAdapter"), new ClickAdapter() {
            @Override
            public void onClick(View view) {
                myMethod(holder.getAdapterPosition(), holder, currentData);
            }
        });
    }

    @CallSuper
    @Override
    public int getItemCount() {
        return data.size();
    }

    @Override
    public void setData(List data) {
        this.data.clear();
        this.data.addAll(data);
        notifyDataSetChanged();
    }

    public abstract Void myMethod(int position, BindingViewHolder holder, Object object);
}

Don’t worry, ViewTypeEnum isn’t something internal, it’s enum generated for each RecyclerView.Adapter to map view types with data. You can see it live when you’ll try it by your own.

All you need to do now it’s simply

TestAdapterGenerated adapter = new TestAdapterGenerated() {
    @Override
    public Void myMethod(int position, BindingViewHolder holder, Object object) {
        // DO SOME FANCY STUFF ONCLICK
        return null;
    }
};
recyclerView.setAdapter(adapter);`

`adapter.setData(...);
// OR
yourBinding.setData(data); // where "data" is a list, your XML contains variable "data" of type List and your RecyclerView have "app:src="@{data}"" attribute.

Isn’t it cool, ha?

To try it, just paste this in your modules build.gradle file

compile 'com.ekalips:adapter-annotation:0.1.1'
annotationProcessor 'com.ekalips:adapter-processor:0.1.1'

And this to your projects build.gradle

maven {
    url "http://dl.bintray.com/ekalips/Bapter"
}

Just be sure, that DataBinding is enabled in your project.

For additional info and feedback leaving visit my GitHub project’s page

You can Contact us anytime to ask or discuss anything