Recycler View and Adapter with Model pattern for Android
Recycler View is used as the List View. The difference between the two is that the RecyclerView is using less hardware resources for showing the items in the list. For example, if you have list with 500 items, its not wise for the app to load every time the whole list. The Recycler View is loading items as the user swipes, up or down so, the list is not populated all of the time, but the user have feeling that it is, and the hardware is not overloaded.
As for the Adapter, is used for populating programmatically the items in the list, what it shows and what it does.
The populating the item in the list we will use a Model class for storing the info that we will need to pass to the Adapter and finally the RecyclerView to show us in the activity, or the fragment or the view that we create. In our case we will use Activity, or more precisely, MainActivity.
In the end we must have hardcoded List<> of Models so we must pass to the Adapter. This list can come from everywhere, like, in our case, hardcoded, or some other activities or some web services.
Here is the example, step by step explanations:
First we need to set the RecyclerView in our activity_layout.xml in Linear Layout. You can use Relative Layout or something else if you like.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
</pre> <pre><?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.xtrid.recyclerviewadapter.MainActivity" android:background="@color/colorPrimaryDark"> <android.support.v7.widget.RecyclerView android:id="@+id/myRecyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginEnd="15dp" android:layout_marginLeft="15dp" android:layout_marginStart="15dp" android:layout_marginRight="15dp"> </android.support.v7.widget.RecyclerView> </LinearLayout></pre> <pre> |
After this, we need to define the Model so it can hold our data. The model will have two Text Fields for name and address and one Button, just for example. The string fields will be declaring public so we can access them from anywhere in our app, and final so that string that can only be assigned once, in our constructor. So, when we create an object from Person class we will be obliged to give it two strings as parameters, name and address. The Model (Person) will look like this:
1 2 3 4 5 6 7 8 9 10 |
public class Person { public final String name; public final String address; public Person(String name, String address) { this.name = name; this.address = address; } } |
We need to make the layout for the item to be shown in the Recycler View. The layout will have two Text Fields and one Button in layout/item_person.xml. It is in Relative Layout overall, two text views wrapped in Linear Layout, and a Button on the far right side. (For the layouts, in detail, in some of the next posts):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
</pre> <pre><?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/myPersonContainer" android:layout_width="match_parent" android:layout_height="70dp" android:orientation="horizontal"> <Button android:id="@+id/button" android:layout_width="90dp" android:layout_height="match_parent" android:layout_alignParentEnd="true" android:text="@string/button" android:layout_alignParentRight="true" /> <LinearLayout android:layout_toStartOf="@id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:orientation="vertical" android:layout_toLeftOf="@id/button"> <TextView android:id="@+id/name" android:textAllCaps="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:ellipsize="end" android:maxLines="1" android:textSize="16sp" tools:ignore="MissingPrefix" /> <TextView android:id="@+id/address" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginTop="3dp" android:ellipsize="end" android:maxLines="1" android:textSize="12sp" /> </LinearLayout> </RelativeLayout></pre> <pre> |
After all of this is done, now we can jump to creating the Adapter for our app. First of all, we need to create PersonAdapter class that’s extends from RecyclerView.Adapter. As type in the <> brackets we will add PersonAdapter.ViewHolder, and ViewHolder will be our inner class, which will be explained later. After this we will need to override 3 methods from RecyclerView.Adapter:
– onCreateViewHolder() for inflating the layout that we create earlier item_person.xml
– onBindViewHolder() for binding functionalities that we will define in our inner ViewHolder class
– getItemCount() for getting the items in the List that we are going to give to the RecyclerView.
Then we create ViewHolder inner class that extends RecyclerView.ViewHolder that must create constructor matching super. In our PersonAdapter class we will add our own method addAll(List personModelList) so we can add the whole list to the adapter.
In our PersonAdapter we must declare a List of type Person for our model list, and a Context so the adapter will know in witch activity, fragment or view we want to initialize the list. This two fields are initialized in the PersonAdapter constructor, for context we use this, and for the list we initialize the list in it.
In onCreateViewHolder(), which is overridden method and receive parameters like ViewGroup parent and int viewType, which will be used to inflate the view for the item.
In onBindViewHolder() we bind the view for the item, with its parameters, views, like TextView, Buttons or whatever we like, and implement the functionality of the item in the RecyclerView, and that is accomplished by a method called from ViewHolder class bind().
In getItemCount() we return the length of the list, so the RecyclerView and the Adapter will know how many items the list have.
In our addAll() method, which takes List as parameter, so we can manipulate with the List, or to clear, to add items, and notify the adapter if items are added or deleted from the list.
In our inner class ViewHolder, we declare all of the views in the item, the Person model and one int which will be the position of the item. We initialize two Text Views, name and address, and initialize the Button. In the constructor we declare ButterKnife.bind() so the views are imported to the parent activity. Bind method is for functionality implementation of the item, and there we will set the name and address fields when adapter received the List.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
public class PersonAdapter extends RecyclerView.Adapter&lt;PersonAdapter.ViewHolder&gt; { private List&lt;Person&gt; personModelList; Context context; PersonAdapter(Context context) { this.context = context; personModelList = new ArrayList&lt;&gt;(); } @Override public PersonAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_person, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(PersonAdapter.ViewHolder holder, int position) { Person personModel = personModelList.get(position); holder.bind(personModel, position); } @Override public int getItemCount() { return personModelList.size(); } public void addAll(List&lt;Person&gt; myListsModel) { personModelList.clear(); personModelList.addAll(myListsModel); notifyDataSetChanged(); } public class ViewHolder extends RecyclerView.ViewHolder { @BindView(R.id.name) TextView name; @BindView(R.id.address) TextView address; @BindView(R.id.button) Button button; Person personModel; int position; @OnClick(R.id.button) public void onClickButton() { Toast.makeText(context, "Button clicked!", Toast.LENGTH_SHORT).show(); } public ViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } public void bind(Person personModel, int position) { this.personModel = personModel; this.position = position; name.setText(personModel.name); address.setText(personModel.address); } } } |
Finally, we implement functionality of the RecyclerView and Adapter in our activity. First declare the recyclerView from activity_layout.xml and PersonAdapter. We create our own methods for setting up user interface setupUi() and method for hardcoded list of items getHardcodedList().
In setupUi() we create the layout manager to handle the separation of item in the RecyclerView, give the adapter all items in the list, set the recyclerView and use the Divider Item Decoration to divide the items in the recyclerView horizontally by line.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
public class MainActivity extends AppCompatActivity { @BindView(R.id.myRecyclerView) RecyclerView myRecyclerView; PersonAdapter personAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); setupUi(); } private void setupUi() { LinearLayoutManager layoutManager = new LinearLayoutManager(this); personAdapter = new PersonAdapter(this); personAdapter.addAll(getHardcodedCategoryList()); myRecyclerView.setAdapter(personAdapter); myRecyclerView.setLayoutManager(layoutManager); DividerItemDecoration itemDivider = new DividerItemDecoration(this, layoutManager.getOrientation()); myRecyclerView.addItemDecoration(itemDivider); } private List&lt;Person&gt; getHardcodedCategoryList() { List&lt;Person&gt; personList = new ArrayList&lt;&gt;(); Person p1 = new Person("Viktor Jovanovski", "Veles, Macedonia"); Person p2 = new Person("Name Surname", "Veles, Macedonia"); Person p3 = new Person("Text Txt", "Veles, Macedonia"); Person p4 = new Person("Happy Coding", "Veles, Macedonia"); Person p5 = new Person("Hardoded List", "Veles, Macedonia"); personList.add(p1); personList.add(p2); personList.add(p3); personList.add(p4); personList.add(p5); return personList; } } |
NOTE: Im using ButterKnife library for binding the views, like buttons, text, etc.