In our last article we discussed about Android RecyclerView showing how it works. But that was a simple example to start with. We saw how phone calls objects are being displayed. Now, what if we have to display multiple type of objects? For say, if we want to display messages/SMS with call logs? As we created a ViewHolder showing call logs data, is it possible to show messages along with that? Yes, it is possible, but in a other way i.e. using multiple ViewHolders. We need just little bit of change to our previous application.
To achieve our goal we need to create another ViewHolder so that message/SMS objects can get reference to it display as required.
CHANGES TO PREVIOUS CODE
We will change some parts of our previous code for Android RecyclerView. Here are the changes we need to do in a brief.
- Create message or SMS object.
- Add some demo SMS object to our list to display them.
- Create layout for SMS for Adapter cell.
- Create another ViewHolder for SMS type.
- Identify object type(whether call or SMS).
- After identification attach particular ViewHolder by object type.
- Display data for single cell by object type.
Enough readings, Right? Now let’s start coding. We are skipping the basic part to set up a RecyclerView. If you are not familiar with it, just read our previous article about Android RecyclerView. We will be focusing in multiple viewholders only.
First of all we will create our SMS object. This looks like –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.digitstory.testapplication; public class SMS { private String senderName,smsContent,smsTime; public SMS(String senderName, String smsContent, String smsTime) { this.senderName = senderName; this.smsContent = smsContent; this.smsTime = smsTime; } public String getSenderName() { return senderName; } public String getSmsContent() { return smsContent; } public String getSmsTime() { return smsTime; } } |
Next, let’s add some demo SMS objects along with our call objects in RecyclerViewActivity. We will mix up them in order to experiment multiple viewholders working properly or not. Here we go –
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 |
package com.digitstory.testapplication; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import java.util.ArrayList; import java.util.List; public class RecyclerViewActivity extends AppCompatActivity { private RecyclerView recyclerView; private LinearLayoutManager linearLayoutManager; private List<Object> callSMSList; private CallSMSAdapter callSMSAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler_view); recyclerView=(RecyclerView)findViewById(R.id.recyclerView); // Set Layout Manager linearLayoutManager=new LinearLayoutManager(this); recyclerView.setLayoutManager(linearLayoutManager); // Limiting the size recyclerView.setHasFixedSize(true); // Initialize list items init(); } private void init(){ callSMSList =new ArrayList<>(); // Initiating Adapter callSMSAdapter =new CallSMSAdapter(RecyclerViewActivity.this); recyclerView.setAdapter(callSMSAdapter); // Adding some demo data(Call & SMS Objects). // You can get them from your data server callSMSList.add(new Call("John","9:30 AM")); callSMSList.add(new Call("Rob","9:40 AM")); callSMSList.add(new SMS("Sandy","Hey, what's up?","9:42 AM")); callSMSList.add(new Call("Peter","9:45 AM")); callSMSList.add(new SMS("John","Are you writing blog?","9:48 AM")); callSMSList.add(new Call("Jack","9:50 AM")); callSMSList.add(new Call("Bob","9:55 AM")); callSMSList.add(new SMS("Kora","Thanks dude","9:57 AM")); callSMSList.add(new Call("Sandy","10:00 AM")); callSMSList.add(new Call("Kate","10:05 AM")); callSMSList.add(new SMS("Nick","Let's hang up","10:10 AM")); callSMSList.add(new Call("Roger","10:15 AM")); callSMSList.add(new Call("Sid","10:20 AM")); callSMSList.add(new Call("Kora","10:25 AM")); callSMSList.add(new Call("Nick","10:30 AM")); callSMSList.add(new SMS("Rose","Bring me some chocolates","1035:10 AM")); callSMSList.add(new Call("Mia","10:40 AM")); callSMSList.add(new Call("Scott","10:45 AM")); // Set items to adapter callSMSAdapter.setCallSMSFeed(callSMSList); callSMSAdapter.notifyDataSetChanged(); } } |
As you can see in the above snippet, we have added some SMS objects in our feed. Now it’s time to design how the cell for SMS will look like. We already defined layout for calls(Check this article). So the code for SMS type layout will be –
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 |
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" app:cardCornerRadius="5dp" android:layout_margin="3dp" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:padding="10dp" android:orientation="horizontal" android:gravity="center_vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:src="@drawable/ic_sms" android:layout_width="30dp" android:layout_height="30dp" /> <RelativeLayout android:layout_marginLeft="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:textSize="18sp" android:textColor="#E91E63" android:id="@+id/senderName" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:textSize="16sp" android:textColor="#000" android:layout_marginTop="2dp" android:layout_below="@+id/senderName" android:id="@+id/smsContent" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:textSize="14sp" android:textColor="#666" android:layout_marginTop="2dp" android:layout_below="@+id/smsContent" android:id="@+id/smsTime" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout> </LinearLayout> </android.support.v7.widget.CardView> |
We have changed it a little with color to differentiate it with call cell layout.
ADAPTER FOR MULTIPLE VIEWHOLDERS
Next, we will modify our adapter a little to implement multiple viewholders. After changing it will look like below
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
package com.digitstory.testapplication; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.ArrayList; import java.util.List; public class CallSMSAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private final static int TYPE_CALL=1,TYPE_SMS=2; private List<Object> callSMSFeed =new ArrayList(); // Context is not used here but may be required to // perform complex operations or call methods from outside private Context context; // Constructor public CallSMSAdapter(Context context){ this.context=context; } public void setCallSMSFeed(List<Object> callSMSFeed){ this.callSMSFeed = callSMSFeed; } // We need to override this as we need to differentiate // which type viewHolder to be attached // This is being called from onBindViewHolder() method @Override public int getItemViewType(int position) { if (callSMSFeed.get(position) instanceof Call) { return TYPE_CALL; } else if (callSMSFeed.get(position) instanceof SMS) { return TYPE_SMS; } return -1; } // Invoked by layout manager to replace the contents of the views @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { int viewType=holder.getItemViewType(); switch (viewType){ case TYPE_CALL: Call call=(Call)callSMSFeed.get(position); ((CallViewHolder)holder).showCallDetails(call); break; case TYPE_SMS: SMS sms=(SMS)callSMSFeed.get(position); ((SMSViewHolder)holder).showSmsDetails(sms); break; } } @Override public int getItemCount(){return callSMSFeed.size();} // Invoked by layout manager to create new views @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // Attach layout for single cell int layout = 0; RecyclerView.ViewHolder viewHolder; // Identify viewType returned by getItemViewType(...) // and return ViewHolder Accordingly switch (viewType){ case TYPE_CALL: layout=R.layout.calls_feed_layout; View callsView = LayoutInflater .from(parent.getContext()) .inflate(layout, parent, false); viewHolder=new CallViewHolder(callsView); break; case TYPE_SMS: layout=R.layout.sms_feed_layout; View smsView = LayoutInflater .from(parent.getContext()) .inflate(layout, parent, false); viewHolder=new SMSViewHolder(smsView); break; default: viewHolder=null; break; } return viewHolder; } // First ViewHolder of object type Call // Reference to the views for each call items to display desired information public class CallViewHolder extends RecyclerView.ViewHolder { private TextView callerNameTextView,callTimeTextView; public CallViewHolder(View itemView) { super(itemView); // Initiate view callerNameTextView=(TextView)itemView.findViewById(R.id.callerName); callTimeTextView=(TextView)itemView.findViewById(R.id.callTime); } public void showCallDetails(Call call){ // Attach values for each item String callerName =call.getCallerName(); String callTime =call.getCallTime(); callerNameTextView.setText(callerName); callTimeTextView.setText(callTime); } } // Second ViewHolder of object type SMS // Reference to the views for each call items to display desired information public class SMSViewHolder extends RecyclerView.ViewHolder { private TextView senderNameTextView,smsContentTextView, smsTimeTextView; public SMSViewHolder(View itemView) { super(itemView); // Initiate view senderNameTextView =(TextView)itemView.findViewById(R.id.senderName); smsContentTextView =(TextView)itemView.findViewById(R.id.smsContent); smsTimeTextView =(TextView)itemView.findViewById(R.id.smsTime); } public void showSmsDetails(SMS sms){ // Attach values for each item String senderName =sms.getSenderName(); String smsContent =sms.getSmsContent(); String smsTime =sms.getSmsTime(); senderNameTextView.setText(senderName); smsContentTextView.setText(smsContent); smsTimeTextView.setText(smsTime); } } } |
Clearly, we can see their are two ViewHolders here CallViewHolder & SMSViewHolder and they are being invoked from onCreateViewHolder() method. They are identified by their viewType returned by getItemViewType(two constants : TYPE_CALL & TYPE_SMS) that sets viewType by class type. After they are attached, every cell are being decorated with information by calling showCallDetails() or showSmsDetails() from onBindViewHolder() method. onBindViewHolder() determines by calling received ViewHolder’s getItemViewType() method which we have overwritten as per our requirement (The TYPE_CALL & TYPE_SMS hack).
So, by now, we can expect you have a clear idea of RecyclerView with multiple ViewHolders. Thus we can also implement our adapter for 3,4 or many type of objects. Feel free to comment if you have any query or doubt. Share this article to inspire us.