Recycler View: How I Shrinked My Projects Code over-200 lines -5 classes!
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 :
How they are now:
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:
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);
//