Android RecyclerView with Single and Multiple Selection

Droid By Me
6 min readFeb 18, 2019

In our previous article, we have explained how to implement Android RecyclerView with multiple view types. Now we are here show you how to do single selection and multiple selection in android recyclerview.

Normally, every coder has a requirement for single selection and multi selection functionality in each and every project. We have already single selection functionality in android as RadioGroup as controller. For multi selection, we haven’t any default controller. For that we have to do manually.

As we know, android recyclerview is of list type controller. So we can do single selection and multi selection feature by this controller. so let’s begin now.

  • Single Selection in Android RecyclerView

For single selection, we just need to hold the position that clicked in the recyclerview. When user clicks the another position, forget previous one and make new position as hold position. Make a new position on hold is not the big thing to do, but main thing is to forget the previous position. For that we are using notifyItemChanged(position) method of recyclerview.

SingleAdapter.java

package com.droidbyme.recyclerviewselection.adapter;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.droidbyme.recyclerviewselection.R;
import com.droidbyme.recyclerviewselection.model.Employee;

import java.util.ArrayList;

public class SingleAdapter extends RecyclerView.Adapter<SingleAdapter.SingleViewHolder> {

private Context context;
private ArrayList<Employee> employees;
// if checkedPosition = -1, there is no default selection
// if checkedPosition = 0, 1st item is selected by default

private int checkedPosition = 0;

public SingleAdapter(Context context, ArrayList<Employee> employees) {
this.context = context;
this.employees = employees;
}

public void setEmployees(ArrayList<Employee> employees) {
this.employees = new ArrayList<>();
this.employees = employees;
notifyDataSetChanged();
}

@NonNull
@Override
public SingleViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.item_employee, viewGroup, false);
return new SingleViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull SingleViewHolder singleViewHolder, int position) {
singleViewHolder.bind(employees.get(position));
}

@Override
public int getItemCount() {
return employees.size();
}

class SingleViewHolder extends RecyclerView.ViewHolder {

private TextView textView;
private ImageView imageView;

SingleViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textView);
imageView = itemView.findViewById(R.id.imageView);
}

void bind(final Employee employee) {
if (checkedPosition == -1) {
imageView.setVisibility(View.GONE);
} else {
if (checkedPosition == getAdapterPosition()) {
imageView.setVisibility(View.VISIBLE);
} else {
imageView.setVisibility(View.GONE);
}
}
textView.setText(employee.getName());

itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
imageView.setVisibility(View.VISIBLE);
if (checkedPosition != getAdapterPosition()) {
notifyItemChanged(checkedPosition);
checkedPosition = getAdapterPosition();
}
}
});
}
}

public Employee getSelected() {
if (checkedPosition != -1) {
return employees.get(checkedPosition);
}
return null;
}
}

Here, checkedPosition is that hold position. On item click listener, we check that if already position on hold is the same position that we click then we change the checkedPosition to getAdapterPosition. Also we need to call notifyItemChanged(checkedPosition) for unchecked the previous one if any.

Employee.java (POJO class)

package com.droidbyme.recyclerviewselection.model;

public class Employee {

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

Activity.java

package com.droidbyme.recyclerviewselection.activity;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatButton;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

import com.droidbyme.recyclerviewselection.R;
import com.droidbyme.recyclerviewselection.adapter.SingleAdapter;
import com.droidbyme.recyclerviewselection.model.Employee;

import java.util.ArrayList;

public class SingleSelectionActivity extends AppCompatActivity {

private android.support.v7.widget.RecyclerView recyclerView;
private ArrayList<Employee> employees = new ArrayList<>();
private SingleAdapter adapter;
private android.support.v7.widget.AppCompatButton btnGetSelected;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_selection);
this.btnGetSelected = (AppCompatButton) findViewById(R.id.btnGetSelected);
this.recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

getSupportActionBar().setTitle("Single Selection");

recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
adapter = new SingleAdapter(this, employees);
recyclerView.setAdapter(adapter);

createList();

btnGetSelected.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (adapter.getSelected() != null) {
showToast(adapter.getSelected().getName());
} else {
showToast("No Selection");
}
}
});
}

private void createList() {
employees = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Employee employee = new Employee();
employee.setName("Employee " + (i + 1));
employees.add(employee);
}
adapter.setEmployees(employees);
}

private void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}

activity_single_selection.xml

<?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"
android:orientation="vertical"
tools:context=".activity.SingleSelectionActivity">

<android.support.v7.widget.AppCompatButton
android:id="@+id/btnGetSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_gravity="center"
android:text="Get Selected" />

<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent">

</android.support.v7.widget.RecyclerView>

</LinearLayout>

item_employee.xml (recyclerview item layout)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="20dp">

<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="18sp" />

<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_tick_point"
android:visibility="gone" />
</LinearLayout>

In above example, you can check single selection. And method getSelected() in adapter returns the selected employee from recyclerview. Check the below output:

  • Multi Selection in Android RecyclerView

For multi selection, we need to add one variable in our POJO (Employee.java) named isChecked as boolean. Now the logic is when user clicks any item of the recyclerview, we just set to true this field. And in bind() method, first we check this flag (true or false) then we show check image depends on that.

Now the coder’s question is if we scroll recyclerview, how the value of that boolean remains same. Because while scrolling recyclerview, onBindViewHolder called at every position. So the value of each item changed to default value at we create those list or fetching from API response. So the answer is Serializable interface.

Serializable interface is used to allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype’s public, protected, and (if accessible) package fields. It has two methods writeObject and readObject. The writeObject method is responsible for writing the state of the object for its particular class so that the corresponding readObject method can restore it. The default mechanism for saving the Object’s fields can be invoked by calling out.defaultWriteObject.

So Serializable interface store the state of that particular class. Now employee class is as below:

package com.droidbyme.recyclerviewselection.model;

import java.io.Serializable;

public class Employee implements Serializable {

private boolean isChecked = false;

public boolean isChecked() {
return isChecked;
}

public void setChecked(boolean checked) {
isChecked = checked;
}

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

MultiAdapter.java

package com.droidbyme.recyclerviewselection.adapter;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.droidbyme.recyclerviewselection.R;
import com.droidbyme.recyclerviewselection.model.Employee;

import java.util.ArrayList;

public class MultiAdapter extends RecyclerView.Adapter<MultiAdapter.MultiViewHolder> {

private Context context;
private ArrayList<Employee> employees;

public MultiAdapter(Context context, ArrayList<Employee> employees) {
this.context = context;
this.employees = employees;
}

public void setEmployees(ArrayList<Employee> employees) {
this.employees = new ArrayList<>();
this.employees = employees;
notifyDataSetChanged();
}

@NonNull
@Override
public MultiViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.item_employee, viewGroup, false);
return new MultiViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull MultiViewHolder multiViewHolder, int position) {
multiViewHolder.bind(employees.get(position));
}

@Override
public int getItemCount() {
return employees.size();
}

class MultiViewHolder extends RecyclerView.ViewHolder {

private TextView textView;
private ImageView imageView;

MultiViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textView);
imageView = itemView.findViewById(R.id.imageView);
}

void bind(final Employee employee) {
imageView.setVisibility(employee.isChecked() ? View.VISIBLE : View.GONE);
textView.setText(employee.getName());

itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
employee.setChecked(!employee.isChecked());
imageView.setVisibility(employee.isChecked() ? View.VISIBLE : View.GONE);
}
});
}
}

public ArrayList<Employee> getAll() {
return employees;
}

public ArrayList<Employee> getSelected() {
ArrayList<Employee> selected = new ArrayList<>();
for (int i = 0; i < employees.size(); i++) {
if (employees.get(i).isChecked()) {
selected.add(employees.get(i));
}
}
return selected;
}
}

MultiSelectionActivity.java

package com.droidbyme.recyclerviewselection.activity;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatButton;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

import com.droidbyme.recyclerviewselection.R;
import com.droidbyme.recyclerviewselection.adapter.MultiAdapter;
import com.droidbyme.recyclerviewselection.model.Employee;

import java.util.ArrayList;

public class MultipleSelectionActivity extends AppCompatActivity {

private android.support.v7.widget.RecyclerView recyclerView;
private ArrayList<Employee> employees = new ArrayList<>();
private MultiAdapter adapter;
private android.support.v7.widget.AppCompatButton btnGetSelected;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_multiple_selection);
this.btnGetSelected = (AppCompatButton) findViewById(R.id.btnGetSelected);
this.recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

getSupportActionBar().setTitle("Multiple Selection");
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
adapter = new MultiAdapter(this, employees);
recyclerView.setAdapter(adapter);

createList();

btnGetSelected.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (adapter.getSelected().size() > 0) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < adapter.getSelected().size(); i++) {
stringBuilder.append(adapter.getSelected().get(i).getName());
stringBuilder.append("\n");
}
showToast(stringBuilder.toString().trim());
} else {
showToast("No Selection");
}
}
});
}

private void createList() {
employees = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Employee employee = new Employee();
employee.setName("Employee " + (i + 1));
// for example to show at least one selection
if (i == 0) {
employee.setChecked(true);
}
//
employees.add(employee);
}
adapter.setEmployees(employees);
}

private void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}

In above example, you can check multi selection. And method getSelected() in adapter returns the list of selected employee from recyclerview. Check the below output:

Find source here.

Hope you like this article. See you later!

--

--