Recycler View: How I Shrinked My Projects Code over-200 lines -5 classes!

Hasan Badran
4 min readJun 30, 2019

--

src: https://thenextweb.com/google/2019/01/07/android-q-might-get-a-system-wide-dark-mode/

Hello !

As most of my projects contained a lot of recycler views I noticed that I write a lot of redudant code for recycler view Adapter classes here and there until that night I have decided to put limit for this spaghetti so I decided to generify things out.

Note : I Assume that You’re using MVP,MVVM,MVPVM architecture + Passive View Approach. If you don’t know what are those please refer to the end of the article.

How my recycler view classes was :

News Adapter (Passive Views Architecture)
Marks Adapter (Passive Views Architecture)

How they are now:

Cool Code Snippet

so what I’ve done is:

1-Create a base recycler view adapter class BaseRecyclerViewAdapter.java

2-Create an interface so we can unify things:

public interface BaseViewHolder {
interface Binder<T>{
void bindViewHolders(T viewHolder,int pos);
int getViewHoldersCount();
}

}

3- Make our BaseRecyclerViewAdapter class extends a generic class that also extends ViewHolder

public class BaseRecyclerViewAdapter<T extends ViewHolder> extends RecyclerView.Adapter<T>

4- Now we need to implement methods of RecyclerView.Adapter, they are:

T onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
void onBindViewHolder(@NonNull T holder, int position)
int getItemCount()

5- onCreateViewHolder return value is T Generic but can we return a (new T()) ? no because it can’t be instantiated directly so we’ll do a little workaround is by creating a Factory method for that T.

T is a class that extends ViewHolder and it needs a View as a parameter :/ ! fourtionatly we also have a workaround for it :D

Solution : Create a factory method named newInstance with T return type and write in it :

private T newInstance(View view) {    try {
return parameterClass.getDeclaredConstructor(View.class).newInstance(view);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

parameterClass is a variable declared in BaseRecyclerViewAdapter class of type Class<T> , we will fit into this variable our ViewHolder after that in newInstance method we will call

.getDeclaredConstructor(View.class) 

which will find our constructor in our ViewHolder which usually looks like this:

ViewHolder Constructor

and we will call

.newInstance(view);

it means : if you find the constructor that have View.class object parameter in ViewHolder Class passed in parameterClass variable then please create me a new instance of ViewHolder Class using that constructer and for itemView take the view variable inside .newInstance and pass it to constructer.

6- now insert those vars into BaseRecyclerViewAdapter class(we’ll get into them later) :

private BaseViewHolder.Binder<T> binder;
@LayoutRes
private int layoutRes;

binder var : we will fit into it the class where we will bind our models into cells ,ex: setText setImage setDescription

layoutRes var : we will fit into it a reference to our cell layout, ex: R.layout.cell_news

7- implement getItemCount() and onBindViewHolder(…) as below:

@Override
public void onBindViewHolder(@NonNull T holder, int position) {
binder.bindViewHolders(holder,position);
}

@Override
public int getItemCount() {
return binder.getViewHoldersCount();
}

8- How we are gonna set those variables ? Binder, layoutRes and parameterClass

Solution: create the following methods

public BaseRecyclerViewAdapter<T> addBinder(BaseViewHolder.Binder<T> binder){
this.binder = binder;
return this;
}
public BaseRecyclerViewAdapter<T> setLayout(int LayoutRes) {
layoutRes = LayoutRes;
return this;
}
public BaseRecyclerViewAdapter<T> setViewHolder(Class<T> typeParameterClass) {
this.parameterClass = typeParameterClass;
return this;
}

Now, We have our generic adapter ready and we need to setup things out…

in our fragment/activity insert :

//this has replaced the ugly adapter
BaseRecyclerViewAdapter<NewsViewHolder> baseRecyclerViewAdapter = new BaseRecyclerViewAdapter<>();
baseRecyclerViewAdapter
.addBinder((BaseViewHolder.Binder<NewsViewHolder>) presenter)
.setLayout(R.layout.cell_news)
.setViewHolder(NewsViewHolder.class);
//
myRecyclerView.setAdapter(baseRecyclerViewAdapter);

NewsViewHolder is a simple ViewHolder that contains TextViews, ImageViews, etc…

presenter variable is my presenter that contains the logic and my models so we’ll let it implement the interface we created at first:

BaseViewHolder.Binder<ViewHolderNewsContract>

my ViewHolderNewsContract class is interface to preserve the architecture MVPVM I’m following:

All of the below has nothing to do with main idea, I put them just to clear things out…

my NewsViewHolder class:

and in my presenter I have implemented the interface as following:

Now if you have 2 recycler views in 1 fragment , you can implement the BaseViewHolder.Binder<ViewHolder> twice in presenter, setup things in fragment and that’s it…

Passive View Approach for recycler view : https://android.jlelse.eu/recyclerview-in-mvp-passive-views-approach-8dd74633158

Thanks for reading !

Github refs:

RecyclerViewAdapter Class:

Binder Interface:

To integrate it with your projects simple add these to classes and implement Binder<T> where T is a class extends ViewHolderClass which you created and in fragment:

//this has replaced the ugly adapter
BaseRecyclerViewAdapter<NewsViewHolder> baseRecyclerViewAdapter = new BaseRecyclerViewAdapter<>();
baseRecyclerViewAdapter
.addBinder((BaseViewHolder.Binder<NewsViewHolder>) presenter)
.setLayout(R.layout.cell_news)
.setViewHolder(NewsViewHolder.class);
//

--

--

Hasan Badran

Senior Android Developer | Cyber Security Researcher