Android RecyclerView MultiViewType

RecyclerView is a ViewGroup ,that display a scrolling list of elements based on large data sets (or data that frequently changes). RecyclerView widget is more flexible and efficient version of ListView .

In the previous series of tutorials we have seen Basic Android Recyclerview , In Basic RecyclerView we have only one ViewHolder which represents layout for each row . Suppose if you want to have different layout at some particular position or at alternative position (even /odd) in such case you have to create different viewHolder holder .  

In this tutorial we will see recyclerView with mutltiViewType , mutltiViewType is nothing but having different viewHolder .




ViewHolder


 Each Row in the list represent ViewHolder Object ,Each view holder is in charge of displaying a single item with a view. you must define a viewholder class which mush extend RecyclerView.ViewHolder .


Add RecyclerView to your layout


file : activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/rcv"/>

Data Class


To Load Data Into RecyclerView , We will read JSON File Kept Inside Asset folder and map it to below defined data class

file : response.json


file : PostModel.kt
package com.tutorialsbuzz.recyclerviewmultipleview

data class PostModel(val postInfo: PostInfo, val comments: MutableList<Comments>) {}

file : PostInfo.kt
package com.tutorialsbuzz.recyclerviewmultipleview

data class PostInfo(val userName: String, val postTime: String, val postDescription: String, val postImage: String) {
}

file : Comments.kt
package com.tutorialsbuzz.recyclerviewmultipleview

data class Comments(val userName: String, val postTime: String, val comment: String) {
}

Adapter and ViewHolder For RecyclerView .


 1.XML Layout for each viewHolder 

1.a PostViewHolder Layout 
Create  XML Layout file in res/layout and name it row_item.xml ,This Layout defines the layout for Items of RecyclerView . Here In this example we have two TextView inside LinearLayout .

file : post_view.xml
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:gravity="center_vertical"
        android:orientation="horizontal">

    <com.tutorialsbuzz.twitterthreadrecyclerview.CircleImageView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:id="@+id/profile_pic"
            android:src="@mipmap/ic_launcher"/>

    <TextView android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="Name"
              android:id="@+id/userName"
              android:layout_marginLeft="10dp"
              android:textColor="@android:color/black"
              android:layout_toEndOf="@+id/profile_pic"/>

    <TextView android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="12 hours ago"
              android:layout_toEndOf="@+id/profile_pic"
              android:layout_marginLeft="10dp"
              android:id="@+id/post_time"
              android:layout_below="@+id/userName"/>

    <TextView android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="New Profile Pic"
              android:padding="8dp"
              android:textColor="@android:color/black"
              android:id="@+id/post_description"
              android:layout_below="@+id/profile_pic"/>

    <ImageView android:layout_width="match_parent"
               android:layout_height="270dp"
               android:layout_below="@+id/post_description"
               android:id="@+id/post_img"
               android:background="#e5e5e5"
               android:padding="10dp"
               android:contentDescription="@string/app_name"
    />

    <TextView android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_marginTop="10dp"
              android:layout_below="@+id/post_img"
              android:text="Comments 44"
              android:textColor="@android:color/black"
              android:id="@+id/commentLabel"/>

    <LinearLayout android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:orientation="horizontal"
                  android:gravity="center"
                  android:paddingTop="10dp"
                  android:paddingBottom="10dp"
                  android:layout_below="@+id/commentLabel">

        <EditText android:layout_width="0dp"
                  android:layout_height="wrap_content"
                  android:layout_weight="1"
                  android:hint="Add Comment"
                  android:id="@+id/comment_box"/>

        <ImageView
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:id="@+id/addComment"
                android:contentDescription="@string/app_name"
                android:src="@drawable/ic_send_black_24dp"/>
    </LinearLayout>

</RelativeLayout>
1.b CommentViewHolder Layout 
Create  XML Layout file in res/layout and name it row_item.xml ,This Layout defines the layout for Items of RecyclerView . Here In this example we have two TextView inside LinearLayout .

file : comment_view.xml
<RelativeLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:paddingStart="10dp"
        android:paddingTop="20dp"
        android:paddingEnd="10dp"
        android:paddingBottom="20dp">

    <com.tutorialsbuzz.twitterthreadrecyclerview.CircleImageView
            android:id="@+id/comment_profile_pic"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="@drawable/circular_draw"
            android:padding="5dp"
            android:src="@drawable/user_dp" />

    <TextView
            android:id="@+id/comment_username"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_toEndOf="@+id/comment_profile_pic"
            android:text="UserName" />

    <TextView
            android:id="@+id/commentTime"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_toEndOf="@id/comment_username"
            android:text=". 7 hours ago" />

    <TextView
            android:id="@+id/commentDesc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/comment_username"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="8dp"
            android:layout_toEndOf="@+id/comment_profile_pic"
            android:text="Comments Comments"
            android:textColor="@android:color/black" />

</RelativeLayout>
2. ViewHolder

2.a PostViewType
Create Inner class PostViewType inside CustomAdapter class extend it to RecyclerView.ViewHolder , This pass above defined xml layout post_view to its construct  add bind function which takes above defined model data class object .

inner class PostViewType(itemView: View) : RecyclerView.ViewHolder(itemView) {

        init {
            itemView.addComment.setOnClickListener(View.OnClickListener {
                postModel.comments.add(
                    Comments(
                        "Guest",
                        "Just Now",
                        itemView.comment_box.text.toString()
                    )
                )
                itemView.comment_box.setText("")
                notifyItemChanged(0)
                notifyItemInserted(postModel.comments.lastIndex)
            })
        }

        fun bindPostView(postInfo: PostInfo, context: Context): Unit {
            val id =
                context.resources.getIdentifier(postInfo.postImage, "drawable", context.packageName)
            itemView.profile_pic.setImageResource(id)
            itemView.commentLabel.text = "Comments : " + (itemCount - 1)
            itemView.post_img.setImageResource(id)
            itemView.post_time.text = postInfo.postTime
            itemView.userName.text = postInfo.userName
        }
    }
2.b CommentViewHolder
Create Inner class CommentViewHolder inside CustomAdapter class extend it to RecyclerView.ViewHolder ,  This pass above defined xml layout comment_view to
its construct  add bind function which takes above defined model data class object .

inner class CommentViewType(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bindCommentView(comments: Comments, context: Context): Unit {
            var id = context.resources.getIdentifier(
                comments.userName.toLowerCase(),
                "drawable",
                context.packageName
            )
            //if id is 0 then set default launcher icon for comment user dp
            if (id == 0)
                id = context.resources.getIdentifier("ic_launcher", "mipmap", context.packageName)

            itemView.comment_profile_pic.setImageResource(id)
            itemView.comment_username.text = comments.userName
            itemView.commentDesc.text = comments.comment
            itemView.commentTime.text = comments.postTime

        }
    }

3. Adapter

Create a Adapter That RecyclerView Can Use ,Create a class CustomAdapter extend it to RecyclerView.Adapter . RecyclerView.Adapter has three abstract methods that we must override .
As we are using two different viewholder for recyclerView ,whic is used to identify viewHolder while creating and binding to viewHolder.

3.a getItemViewType() : 
This mehtod returns integer value , value identifying the type of the view needed to represent the item at position . The position parameter is used to query viewHolder . 
  
Note : default return value is '0' making the assumption of a single view type for the adapter .
    override fun getItemViewType(position: Int): Int {
        if (position == 0) {
            //postView
            return postView
        } else {
            //commentView
            return commentView
        }
    }
3.b onCreateViewHolder()
Inside this method we specify the layout that each item of the RecyclerView should use .onCreateViewHolder has return type of RecyclerView.ViewHolder which represent each row of recyclerView. Using Inflator get the view of above defined layout and pass it to viewholder constructor and then return. This method contains a int parameter(viewType) at runtime check for viewType create ViewHolder instance and return .

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
	val layoutInflater = LayoutInflater.from(parent.context)
	if (viewType == postView) {
		//postview
		return PostViewType(layoutInflater.inflate(R.layout.post_view, parent, false))
	} else {
		//commentview
		return CommentViewType(layoutInflater.inflate(R.layout.comment_view, parent, false))
	}
}	   


3.c onBindViewHolder()
Inside this method do query for viewType and bind data .
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
	if (holder is PostViewType) {
		(holder as PostViewType).bindPostView(postModel.postInfo, context)
	} else {
		(holder as CommentViewType).bindCommentView(
			postModel.comments.get(position - 1),
			context
		)
	}
}

3.d getItemCount()
This method return count (i.e not of item in adapter )  , Here in this tutorial lets say there are 10 comments for a post then total count will  be ( 10comments + 1 postView = 11) 

override fun getItemCount(): Int {
   return postModel.comments.size + 1
}

Custom Adapter


file :CustomAdapter.kt
package com.tutorialsbuzz.kotlinhelloworld

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.tutorialsbuzz.recyclerviewmultipleview.Comments
import com.tutorialsbuzz.recyclerviewmultipleview.PostInfo
import com.tutorialsbuzz.recyclerviewmultipleview.PostModel
import com.tutorialsbuzz.recyclerviewmultipleview.R
import kotlinx.android.synthetic.main.comment_view.view.*
import kotlinx.android.synthetic.main.post_view.view.*


class CustomAdapter(val postModel: PostModel, val context: Context) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    val postView: Int = 1
    val commentView: Int = 2

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        if (viewType == postView) {
            //postview
            return PostViewType(layoutInflater.inflate(R.layout.post_view, parent, false))
        } else {
            //commentview
            return CommentViewType(layoutInflater.inflate(R.layout.comment_view, parent, false))
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is PostViewType) {
            (holder as PostViewType).bindPostView(postModel.postInfo, context)
        } else {
            (holder as CommentViewType).bindCommentView(
                postModel.comments.get(position - 1),
                context
            )
        }
    }

    //this method is giving the size of the list
    override fun getItemCount(): Int {
        return postModel.comments.size + 1
    }

    override fun getItemViewType(position: Int): Int {
        if (position == 0) {
            //postView
            return postView
        } else {
            //commentView
            return commentView
        }
    }

    //ViewHolder for PostView
    inner class PostViewType(itemView: View) : RecyclerView.ViewHolder(itemView) {

        init {
            itemView.addComment.setOnClickListener(View.OnClickListener {
                postModel.comments.add(
                    Comments(
                        "Guest",
                        "Just Now",
                        itemView.comment_box.text.toString()
                    )
                )
                itemView.comment_box.setText("")
                notifyItemChanged(0)
                notifyItemInserted(postModel.comments.lastIndex)
            })
        }

        fun bindPostView(postInfo: PostInfo, context: Context): Unit {
            val id =
                context.resources.getIdentifier(postInfo.postImage, "drawable", context.packageName)
            itemView.profile_pic.setImageResource(id)
            itemView.commentLabel.text = "Comments : " + (itemCount - 1)
            itemView.post_img.setImageResource(id)
            itemView.post_time.text = postInfo.postTime
            itemView.userName.text = postInfo.userName
        }
    }

    //ViewHolder for CommentView
    inner class CommentViewType(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bindCommentView(comments: Comments, context: Context): Unit {
            var id = context.resources.getIdentifier(
                comments.userName.toLowerCase(),
                "drawable",
                context.packageName
            )
            //if id is 0 then set default launcher icon for comment user dp
            if (id == 0)
                id = context.resources.getIdentifier("ic_launcher", "mipmap", context.packageName)

            itemView.comment_profile_pic.setImageResource(id)
            itemView.comment_username.text = comments.userName
            itemView.commentDesc.text = comments.comment
            itemView.commentTime.text = comments.postTime

        }
    }
}


MainActivity

  1. Create a koltin class MainActivity.kt and extend it to AppCompatActivity class .
  2. Override onCreate function and set the content of this MainActivity with above defined xml layout (activity_main.xml). 
  3. Read JSON Data From Asset Folder , this will be used for binding data into RecyclerView .
  4. Set Vertical Linear LayoutManager to RecyclerView.
  5. Create Instance of Adapter and set to RecyclerView .

file :MainActivity.kt
package com.tutorialsbuzz.recyclerviewmultipleview

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson
import com.tutorialsbuzz.kotlinhelloworld.CustomAdapter
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val dataList = readFromAssets();

        val adapter = CustomAdapter(dataList, this);

        rcv.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
        rcv.adapter = adapter;
        rcv.addItemDecoration(SimpleDividerItemDecoration(this))
    }

    private fun readFromAssets(): PostModel {
        val bufferReader = application.assets.open("response.json").bufferedReader()

        val json_string = bufferReader.use {
            it.readText()
        }
        val gson = Gson()
        val postModel = gson.fromJson(json_string, PostModel::class.java)
        return postModel
    }
}




RecyclerView Related
  1. RecyclerView
  2. RecyclerView Item Click Ripple Effect
  3. RecyclerView With Item Divider
  4. RecyclerView With CardView
  5. RecyclerView GridLayout
  6. RecyclerView StaggeredGrid Layout
  7. RecyclerView Swipe To Delete
  8. Android RecyclerView Swipe to Delete Undo
  9. Android RecyclerView Interactive Swipe Like Gmail
  10. Android RecyclerView Buttons Under Swipe

No comments:

Post a comment