Writing a basic RecyclerView.Adapter
and how it is similar to a typical getView() implementation
In my previous article, ListView and ViewHolder, I discussed about the ViewHolder pattern and how it should be combined to be a part of an adapter for a ListView, or more specifically - a part of an adapter's getView()
method.
We had a simple model named Profile, which describes some basic information of a person. It has an image, a name and a short bio:
public class Profile {
/**
* Name of person.
*/
private String mName;
/**
* Bio.
*/
private String mBio;
/**
* Profile image resource.
*/
private int mProfileImageResource;
public Profile(String name, String bio, int profileImageResource) {
mName = name;
mBio = bio;
mProfileImageResource = profileImageResource;
}
/* "Get" methods... */
}
Our adapter, MyArrayAdapter, mapped Profile's three members ( name, bio and image ) to matching Views in our layout (R.layout.list_item
): two TextViews for name and bio and an ImageView for the image. Let's remember that instead of using findViewById()
multiple times ( to find these three Views as getView()
is being called ), we created a ViewHolder and used it to map a Profile to a visual layout.
This mapping process is called binding. We can say that we bind ViewHolder's members ( Our layout's Views ) to a Profile.
If we first create a ViewHolder and then bind a ViewHolder, wouldn't it make sense to split these two tasks into two methods onCreateViewHolder()
and onBindViewHolder()
?
This is exactly what RecyclerView.Adapter does!
RecyclerView.Adapter declares two abstract
methods which we obviously must implement when we extend it:
public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
public abstract void onBindViewHolder(VH holder, int position);
RecyclerView.Adapter is responsible for invoking these two methods whenever there's a need to create or bind a ViewHolder.
You can see that onCreateViewHolder()
returns a VH
type, and I can tell you that onBindViewHolder()
receives this exact returned value as an argument. VH
stands for View Holder and it's a generic type which needs to extend RecyclerView.ViewHolder and which we need to implement. Let's call it MyViewHolder :
class MyViewHolder extends RecyclerView.ViewHolder {
TextView name;
TextView bio;
ImageView img;
MyViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.tv_name);
bio = (TextView) itemView.findViewById(R.id.tv_bio);
img = (ImageView) itemView.findViewById(R.id.iv_profile_image);
}
}
In its constructor, MyViewHolder receives the itemView
argument, which is the inflated layout (R.layout.list_item
) in which and by which we find all other child views: name, bio and img.
MyViewHolder's constructor executes a series of calls to findViewById()
, one for each View in our layout and keeps references to each one of the Views.
To get a better idea on how to implement onCreateViewHolder()
and onBindViewHolder()
having MyViewHolder implementation, let's look at their similarity with our already implemented getView()
in the previous article - ListView and ViewHolder
OK, things are getting more clear now.onBindViewHolder()
's implementation is just a copy-paste task:
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final Profile profile = mProfiles.get(position);
holder.name.setText(profile.getName());
holder.bio.setText(profile.getBio());
holder.img.setImageResource(profile.getProfileImageResource());
}
Finally, we can write onCreateViewHolder()
to create a MyViewHolder and return it, so that onBindViewHolder()
will receive it as an argument later and continue with the binding process:
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View itemView = View.inflate(mContext, R.layout.list_item, null);
return new MyViewHolder(itemView);
}
Connecting all the pieces, we can now write our adapter - MyProfilesAdapter :
class MyProfilesAdapter extends RecyclerView.Adapter<MyProfilesAdapter.MyViewHolder> {
private Context mContext;
private List<Profile> mProfiles = Collections.emptyList();
MyProfilesAdapter(Context context, List<Profile> profiles) {
mContext = context;
mProfiles = profiles;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View itemView = View.inflate(mContext, R.layout.list_item, null);
return new MyViewHolder(itemView);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final Profile profile = mProfiles.get(position);
holder.name.setText(profile.getName());
holder.bio.setText(profile.getBio());
holder.img.setImageResource(profile.getProfileImageResource());
}
@Override
public int getItemCount() {
return mProfiles.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView name;
TextView bio;
ImageView img;
MyViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.tv_name);
bio = (TextView) itemView.findViewById(R.id.tv_bio);
img = (ImageView) itemView.findViewById(R.id.iv_profile_image);
}
}
}