Android RecyclerView - CardView with CheckBox,Images,Text and ItemClick


Kotlin Android RecyclerView with CheckBoxes, Images, text and ItemClick supports Tutorial and Example.

I want to show you how you can work with the recyclerview in this tutorial and do common things like using it with checkboxes, images, textview and also supporting click events. All these we do in a single MainActivity.kt file.

Video Tutorial

If you prefer a video tutorial then check here:

What You Learn

Here are the concepts you learn:

  1. How to use Recyclerview in android with Kotlin Programming Language.
  2. How to use CheckBox with recyclerview and handle the checkbox states appropriately.
  3. How to create a recyclerview with images and text.
  4. How to support item click event in recyclerview.

Tools Used

Here are the tools I used for this project:

  1. Programming Language : Kotlin
  2. Framework - Android
  3. IDE - Android Studio.
  4. Emulator - Nox Player
  5. OS : Windows 8.1

Let's start.

Demo

Here are the demo for this project:

Kotlin Android RecyclerView with CheckBox,Images and Text

Here's the project in landscape mode. Kotlin Android RecyclerView with CheckBox,Images and Text

(a). Build.gradle

Our app level build.gradle. This is where we add our dependencies. Especially we are interested in three main of those:

  1. AppCompat - To give us the AppCompatActivity from which our MainActivity will derive.
  2. Design Support - To give us the RecyclerView.
  3. CardView - To give us Cardview.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    testImplementation 'junit:junit:4.12'
    implementation 'com.android.support:appcompat-v7:28.0.0-alpha3'
    implementation 'com.android.support:design:28.0.0-alpha3'
    implementation 'com.android.support:cardview-v7:28.0.0-alpha3'
}

(b). activity_main.xml

This is our main activity's layout. Here are the components we use:

  1. RelativeLayout - To arrange our other widgets relative to each other.
  2. TextView - To render header text.
  3. RecyclerView - To render our data.
  4. Floating Action Button - To show the checked items when clicked.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.devosha.kotlin_recyclerview_checkbox.MainActivity">

    <TextView
        android:id="@+id/headerTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Spiritual Teachers"
        android:textAlignment="center"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        android:textColor="@color/colorAccent" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/myRecycler"
        class="android.support.v7.widget.RecyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/headerTextView"
        android:layout_marginTop="30dp" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_gravity="bottom|end"
        android:src="@android:drawable/ic_dialog_email" />

</RelativeLayout>

(c). model.xml

This layout will define our item views for our recyclerview, it will model them. Basically it will be rendering data for a single SpiritualTeacher object. At the root we have a CardView. Then we also have:

  1. ImageView - To render image for the SpiritualTeacher object.
  2. TextVIew - To render his name as well as description.
  3. CheckBox - To allow us select and unselect the CardViews.
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_margin="5dp"
    card_view:cardCornerRadius="10dp"
    card_view:cardElevation="5dp"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:id="@+id/teacherImageView"
            android:padding="5dp"
            android:src="@drawable/cle" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Teacher Name"
            android:id="@+id/nameTextView"
            android:padding="5dp"
            android:textColor="@color/colorAccent"
            android:layout_alignParentTop="true"
            android:layout_toRightOf="@+id/teacherImageView" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Teacher Description..."
            android:id="@+id/descritionTextView"
            android:padding="5dp"
            android:layout_alignBottom="@+id/teacherImageView"
            android:layout_toRightOf="@+id/teacherImageView" />

        <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/myCheckBox"
            android:layout_alignParentRight="true"
            />

    </RelativeLayout>
</android.support.v7.widget.CardView>

(d). MainActivity.kt

This is our main activity. This is our only file and we will define several inner kotlin classes here. This class will be deriving from the AppCompatActivity.

Create Data Object

We will start by defining an inner class called SpiritualTeacher:

   inner class SpiritualTeacher(var name: String?, val quote: String, val image: Int) {
        var isSelected: Boolean = false
    }

That class is our data object. It will receive it's parameters via the constructor. Moreover we are maintaining a simple boolean as you can see that will hold for us the selection state of the CardViews. In fact this is the magic to allow us select and unselect our items in our recyclerview. Thus our recyclerview will not lose the state even as we scroll and the recyclerview items get recycled. This is because we are holding them as data in our object and they are not affected by the recycling.

Create RecyclerVew Adapter class

Normally the Adapter has two main roles. First to inflate our custom layouts and secondly to bind data to the resultant widgets. However recyclerview always goes further by allowing us to recycle items. This saves memory consumption as inflation is an expensive process and would impact negatively on our app if we were to do it for every item in the recyclerview.

So we will create an adapter:

    internal class MyAdapter(var c: Context, var teachers: Array<SpiritualTeacher>) : RecyclerView.Adapter<MyAdapter.MyHolder>() {..}

We've made it derive from the RecyclerView.Adapter class.

Inside the adapter first we will create an arraylist that will hold for us the teachers that have been checked:

        var checkedTeachers = ArrayList<SpiritualTeacher>()

It is inside the onCreateViewHolder() method where we will be inflating our model.xml into a View object. For that we use the LayoutInflater class.

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
            val v = LayoutInflater.from(parent.context).inflate(R.layout.model, null)
            return MyHolder(v)
        }

Here's the full source code.

package com.devosha.kotlin_recyclerview_checkbox

import android.content.Context
import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast

import java.util.ArrayList

/**
 * class: MainActivity
 * This class is deriving from `android.support.v7.app.AppCompatActivity`.
 */
class MainActivity : AppCompatActivity() {

    private val teachers: Array<SpiritualTeacher>
        get() =
            arrayOf(SpiritualTeacher("Rumi", "Out beyond ideas of wrongdoing and rightdoing there is a field.I'll meet you there.", R.drawable.rumi),
                    SpiritualTeacher("Anthony De Mello", "Don't Carry Over Experiences from the past", R.drawable.anthony_de_mello),
                    SpiritualTeacher("Eckhart Tolle", "Walk as if you are kissing the Earth with your feet.", R.drawable.eckhart_tolle),
                    SpiritualTeacher("Meister Eckhart", "Man suffers only because he takes seriously what the gods made for fun.", R.drawable.meister_eckhart),
                    SpiritualTeacher("Mooji", "I have lived with several Zen masters -- all of them cats.", R.drawable.mooji),
                    SpiritualTeacher("Confucius", "I'm simply saying that there is a way to be sane. I'm saying that you ", R.drawable.confucius),
                    SpiritualTeacher("Francis Lucille", "The way out is through the door. Why is it that no one will use this method?", R.drawable.francis_lucille),
                    SpiritualTeacher("Thich Nhat Hanh", "t is the power of the mind to be unconquerable.", R.drawable.thich),
                    SpiritualTeacher("Dalai Lama", "It's like you took a bottle of ink and you threw it at a wall. Smash! ", R.drawable.dalai_lama),
                    SpiritualTeacher("Jiddu Krishnamurti", "A student, filled with emotion and crying, implored, 'Why is there so much suffering?", R.drawable.jiddu_krishnamurti),
                    SpiritualTeacher("Osho", "Only the hand that erases can write the true thing.", R.drawable.osho),
                    SpiritualTeacher("Sedata", "Many have died; you also will die. The drum of death is being beaten.", R.drawable.sedata),
                    SpiritualTeacher("Allan Watts", "Where there are humans, You'll find flies,And Buddhas.", R.drawable.allant_watts),
                    SpiritualTeacher("Leo Gura", "Silence is the language of Om. We need silence to be able to reach our Self.", R.drawable.sadhguru),
                    SpiritualTeacher("Rupert Spira", "One day in my shoes and a day for me in your shoes, the beauty of travel lies ", R.drawable.rupert_spira),
                    SpiritualTeacher("Sadhguru", "Like vanishing dew,a passing apparition or the sudden flashnof lightning", R.drawable.sadhguru))
    internal var sb: StringBuilder? = null
    internal lateinit var adapter: MyAdapter

    inner class SpiritualTeacher(var name: String?, val quote: String, val image: Int) {
        var isSelected: Boolean = false
    }

    internal class MyAdapter(var c: Context, var teachers: Array<SpiritualTeacher>) : RecyclerView.Adapter<MyAdapter.MyHolder>() {
        var checkedTeachers = ArrayList<SpiritualTeacher>()

        //VIEWHOLDER IS INITIALIZED
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
            val v = LayoutInflater.from(parent.context).inflate(R.layout.model, null)
            return MyHolder(v)
        }

        //DATA IS BOUND TO VIEWS
        override fun onBindViewHolder(holder: MyHolder, position: Int) {
            val teacher = teachers[position]
            holder.nameTxt.text = teacher.name
            holder.posTxt.text = teacher.quote
            holder.myCheckBox.isChecked = teacher.isSelected
            holder.img.setImageResource(teacher.image)

            holder.setItemClickListener(object : MyHolder.ItemClickListener {
                override fun onItemClick(v: View, pos: Int) {
                    val myCheckBox = v as CheckBox
                    val currentTeacher = teachers[pos]

                    if (myCheckBox.isChecked) {
                        currentTeacher.isSelected = true
                        checkedTeachers.add(currentTeacher)
                    } else if (!myCheckBox.isChecked) {
                        currentTeacher.isSelected = false
                        checkedTeachers.remove(currentTeacher)
                    }
                }
            })
        }

        override fun getItemCount(): Int {
            return teachers.size
        }

        internal class MyHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {

            var img: ImageView
            var nameTxt: TextView
            var posTxt: TextView
            var myCheckBox: CheckBox

            lateinit var myItemClickListener: ItemClickListener

            init {

                nameTxt = itemView.findViewById(R.id.nameTextView)
                posTxt = itemView.findViewById(R.id.descritionTextView)
                img = itemView.findViewById(R.id.teacherImageView)
                myCheckBox = itemView.findViewById(R.id.myCheckBox)

                myCheckBox.setOnClickListener(this)
            }

            fun setItemClickListener(ic: ItemClickListener) {
                this.myItemClickListener = ic
            }

            override fun onClick(v: View) {
                this.myItemClickListener.onItemClick(v, layoutPosition)
            }

            internal interface ItemClickListener {

                fun onItemClick(v: View, pos: Int)
            }
        }
    }

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

        adapter = MyAdapter(this, teachers)

        val fab = findViewById(R.id.fab) as FloatingActionButton
        fab.setOnClickListener {
            sb = StringBuilder()

            var i = 0
            while (i < adapter.checkedTeachers.size) {
                val spiritualTeacher = adapter.checkedTeachers[i]
                sb!!.append(spiritualTeacher.name)
                if (i != adapter.checkedTeachers.size - 1) {
                    sb!!.append("\n")
                }
                i++

            } 

            if (adapter.checkedTeachers.size > 0) {
                Toast.makeText([email protected], sb!!.toString(), Toast.LENGTH_SHORT).show()
            } else {
                Toast.makeText([email protected], "Please Check An Item First", Toast.LENGTH_SHORT).show()
            }
        }

        //RECYCLER
        val rv = findViewById(R.id.myRecycler) as RecyclerView
        rv.layoutManager = LinearLayoutManager(this)
        rv.itemAnimator = DefaultItemAnimator()

        //SET ADAPTER
        rv.adapter = adapter

    }
// end
}

How do You Feel after reading this?

According to scientists, we humans have 8 primary innate emotions: joy, acceptance, fear, surprise, sadness, disgust, anger, and anticipation. Feel free to tell us how you feel about this article using these emotes or via the comment section. This feedback helps us gauge our progress.

Help me Grow.

I set myself some growth ambitions I desire to achieve by this year's end regarding this website and my youtube channel. Am halfway. Help me reach them by:




Recommendations


What do You Think


Previous Post