Android RecyclerView CheckBoxes - Maintain Checked States


This is an android recyclerview checkboxes tutorial. We want to see how to first render checkboxes, images and text in a custom recyclerview with cardviews. Then get the selected or checked cardviews and show n a Toast message. We work with multiple checkboxes so we can multi-select or single-select items in our RecyclerView.

This will teach us how to properly retain checked states of our checkboxes in our recyclerview despite the recycling of our cardviews with checkboxes.

Then the user can click a floating action button to get the selected items and show in a Toast message.

We are Building a Vibrant YouTube Community

We have a fast rising YouTube Channel of friends. So far we've accumulated more than 2.6 million agreggate views and more than 10,000 subscribers. Here's the Channel: ProgrammingWizards TV.

Please go ahead subscribe(free obviously) as well. If you have a question or a comment you can post there instead of in this site.People are suggesting us tutorials to do there so you can too.

Here's this tutorial in Video Format.

Build.gradle

Our app level build.gradle file will comprise of dependencies from support library. These include appcompat, cardview and design support.

You add these in the dependencies section of the file.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    testImplementation 'junit:junit:4.12'
    implementation 'com.android.support:appcompat-v7:26.+'
    implementation 'com.android.support:cardview-v7:26.+'
    implementation 'com.android.support:design:26.+'
}

activity_main.xml

This will be our main activity layout and we write in XML. XML is a markup language that is not only used to create user interfaces but also exchnage data.In this case we use to create our android user interface.

This layout will be inflated into our main activity. At the root we will have a RelativeLayout.

We have a TextView which is our headerTextView to display the heading our android application.

Then we have a RecyclerView which in this case is our adapterview. This RecyclerView will render images, text and checkboxes.

Below the RecyclerView we will have a FloatingActionButton. When users click this FAB button, we will show the items that have been selected in the recyclerview. Remember our recyclerview will be comprising cardviews that have images, text and checkboxes.

<?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="info.camposha.checkablerecyclerviews.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>

model.xml

This layout will model a single cardview to be rendered among other cards in our recyclerview.

This layout will model all these cardviews. At the root we have the CardView as our element. CardView resides in the android.support.v7.widget namespace.

I have set the Card elevation to 5dp and card cornder radius to 10dp.

Inside it I have a RelativeLayout to organize my widgets relative to each other.

First we have the ImageView to render our Image. Then TextViews to render our texts.

To the far right of our CardView we will have a CheckBox that will hold the selection property of each cardview. The CheckBoxes can be checked or unchecked.

<?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>

MainActivity.java

Specify Package and add Imports

Our java class will reside in a package, so we specify one and add the appropriate imports.

These include our RecyclerView from the android.support.v7.widget package and CheckBox from android.widget package.

package info.camposha.checkablerecyclerviews;

import android.content.Context;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
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;

Create Activity

An activity is an android component that representing a user interface.

To turn our ordinary class into an activity, we will make it derive from AppCompatActivity:

public class MainActivity extends AppCompatActivity {..}

Create our POJO

Then we will create a POJO class, Plain OLd Java Object. This will represents a single Teacher object.

Each SpiritualTecaher will have several properties. Each object will be rendered in it's own CardView in our RecyclerView.

    public class SpiritualTeacher {
        //our properties.
    }

One of those properties will be the isSelected property, a boolean value that will hold for us the checked state of our CardView.

Create our View Holder

We will create our RecyclerView ViewHolder class by deriving from the RecyclerView.ViewHolder class.

Also the class will be implementing the View.OnClickListener interface. This will allow us to capture click events of our each CardView.

    static class MyHolder extends RecyclerView.ViewHolder implements View.OnClickListener{...}

Create RecyclerView Adapter

Then we come create our RecyclerView.Adapter class, the class that will be our adapter class.

    static class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyHolder> {..}

As an adapter class this class has two primary roles:

  1. Inflate our Custom Model Layout into a View Object.
  2. Bind data set to o our inflate views.

The data to be bound will be coming from a custom array, in this case an array of SpiritualTeachers:

        public MyAdapter(Context c, SpiritualTeacher[] teachers) {
            this.c = c;
            this.teachers = teachers;
        }

This class will also maintain an arraylist that will hold our checkeed items.

        ArrayList<SpiritualTeacher> checkedTeachers=new ArrayList<>();

Users will check the recyclerview items and we hold those checked items in this arraylist. Then later on we can display these checked items in a Toast message.

Inflate Layout into View

This will take place in our RecyclerView adapter, as we agreed. We need a LayoutInflater class to do this.

        @Override
        public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v= LayoutInflater.from(parent.getContext()).inflate(R.layout.model,null);
            MyHolder holder=new MyHolder(v);
            return holder;
        }

Then as you can see we've passed the inflate view object into our RecyclerView ViewHolder constructor. Then returned the View Holder instance.

Binding Data

We bind data inside the onBindViewHolder method:

        @Override
        public void onBindViewHolder(MyHolder holder, int position) {
            final SpiritualTeacher teacher=teachers[position];
            holder.nameTxt.setText(teacher.getName());
            holder.posTxt.setText(teacher.getQuote());
            holder.myCheckBox.setChecked(teacher.isSelected());
            holder.img.setImageResource(teacher.getImage());
            ...
        }

First we've received the position of the item in our recyclerview and used it to get the current teacher from our array.

Then set TextView, imageView and CheckBox widgets with values from that object.

Maintain Checked and UnChecked Items

We then listen to click events of our CardView. Obviously clicking the CheckBox will change the state of that checkbox.

We check for these changes. If the checkbox is clicked, we will add the item in our arraylist.

If it's unchecked we remove the item from the arraylist.

            holder.setItemClickListener(new MyHolder.ItemClickListener() {
                @Override
                public void onItemClick(View v, int pos) {
                    CheckBox myCheckBox= (CheckBox) v;
                    SpiritualTeacher currentTeacher=teachers[pos];

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

Let's look at the full source code.

Full Code

Here's the full MainActivity code.

package info.camposha.checkablerecyclerviews;

import android.content.Context;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
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;

public class MainActivity extends AppCompatActivity {

    public class SpiritualTeacher {
        private String name,quote;
        private int image;
        private boolean isSelected;

        public SpiritualTeacher(String name, String quote, int image) {
            this.name = name;
            this.quote = quote;
            this.image = image;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getQuote() {
            return quote;
        }
        public int getImage() {
            return image;
        }

        public boolean isSelected() {
            return isSelected;
        }

        public void setSelected(boolean selected) {
            isSelected = selected;
        }
    }
    static class MyHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

            ImageView img;
            TextView nameTxt,posTxt;
            CheckBox myCheckBox;

            ItemClickListener itemClickListener;

            public MyHolder(View itemView) {
                super(itemView);

                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);
            }
            public void setItemClickListener(ItemClickListener ic)
            {
                this.itemClickListener=ic;
            }
            @Override
            public void onClick(View v) {
                this.itemClickListener.onItemClick(v,getLayoutPosition());
            }
            interface ItemClickListener {

                void onItemClick(View v,int pos);
            }
        }
    static class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyHolder> {

        Context c;
        SpiritualTeacher[] teachers;
        ArrayList<SpiritualTeacher> checkedTeachers=new ArrayList<>();

        public MyAdapter(Context c, SpiritualTeacher[] teachers) {
            this.c = c;
            this.teachers = teachers;
        }

        //VIEWHOLDER IS INITIALIZED
        @Override
        public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v= LayoutInflater.from(parent.getContext()).inflate(R.layout.model,null);
            MyHolder holder=new MyHolder(v);
            return holder;
        }

        //DATA IS BOUND TO VIEWS
        @Override
        public void onBindViewHolder(MyHolder holder, int position) {
            final SpiritualTeacher teacher=teachers[position];
            holder.nameTxt.setText(teacher.getName());
            holder.posTxt.setText(teacher.getQuote());
            holder.myCheckBox.setChecked(teacher.isSelected());
            holder.img.setImageResource(teacher.getImage());

            holder.setItemClickListener(new MyHolder.ItemClickListener() {
                @Override
                public void onItemClick(View v, int pos) {
                    CheckBox myCheckBox= (CheckBox) v;
                    SpiritualTeacher currentTeacher=teachers[pos];

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

        @Override
        public int getItemCount() {
            return teachers.length;
        }

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

        return spiritualTeachers;
    }
    StringBuilder sb=null;
    MyAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        adapter=new MyAdapter(this,getTeachers());

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                sb=new StringBuilder();

                int i=0;
                do {
                    SpiritualTeacher spiritualTeacher=adapter.checkedTeachers.get(i);
                    sb.append(spiritualTeacher.getName());
                    if(i != adapter.checkedTeachers.size()-1){
                        sb.append("\n");
                    }
                    i++;

                }while (i < adapter.checkedTeachers.size());

                if(adapter.checkedTeachers.size()>0)
                {
                    Toast.makeText(MainActivity.this,sb.toString(),Toast.LENGTH_SHORT).show();
                }else
                {
                    Toast.makeText(MainActivity.this,"Please Check An Item First", Toast.LENGTH_SHORT).show();
                }
            }
        });

        //RECYCLER
        RecyclerView rv= (RecyclerView) findViewById(R.id.myRecycler);
        rv.setLayoutManager(new LinearLayoutManager(this));
        rv.setItemAnimator(new DefaultItemAnimator());

        //SET ADAPTER
        rv.setAdapter(adapter);

    }

}

Results

I tested my project with Nox Player Emulator.

Android RecyclerView CheckBoxes

Best Regards.

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 Next Post