How to create multi select ListView Android with custom adapter

Author: | Posted in Android, Quick Tips 14 Comments

Here is a tutorial for how to create multi select ListView Android with custom adapter.
In this tutorial we will learn some more things as below.
1. How to create multiple selection ListView.
2. How to create/use custom adapter for multiple selection ListView.
3. How to read contacts from contact book in android.
4. How to implement search feature in ListView android.
5. How to get/use selected data from a custom multiple selection ListView android.

First create a new android project.
and create a list row layout file in your res/layout folder named list_row.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#3c3c3c"
    android:orientation="horizontal"
    android:padding="8dp" >
    <ImageView
        android:id="@+id/contactimage"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_marginRight="8dp"
        android:background="@drawable/ic_launcher"
        android:contentDescription="@string/app_name"
        android:scaleType="centerInside" />
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_toLeftOf="@+id/contactcheck"
        android:layout_toRightOf="@+id/contactimage" >
        <TextView
            android:id="@+id/contactname"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:text="Contact Name"
            android:textColor="#000"
            android:textIsSelectable="false"
            android:textSize="18dp"
            android:textStyle="bold" />
        <TextView
            android:id="@+id/contactno"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/contactname"
            android:singleLine="true"
            android:text="09876543210"
            android:textColor="#2689e0"
            android:textIsSelectable="false"
            android:textSize="14dp" />
    </RelativeLayout>
    <CheckBox
        android:id="@+id/contactcheck"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerInParent="true"
        android:layout_marginLeft="8dp" />
</RelativeLayout>

modify your main activity’s layout file like this(eg.:activity_main.xml)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <EditText
        android:id="@+id/input_search"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerInParent="true"
        android:hint="Search Contacts"
        android:textSize="18dp" />
    <LinearLayout
        android:id="@+id/data_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/ok_button"
        android:layout_below="@+id/input_search"
        android:gravity="center|top"
        android:orientation="vertical" />
    <Button
        android:id="@+id/ok_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerInParent="true"
        android:text=" OK "
        android:textSize="18dp" />
    <RelativeLayout
        android:id="@+id/pbcontainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#55000000"
        android:clickable="true"
        android:visibility="gone" >
        <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true" />
    </RelativeLayout>
</RelativeLayout>

and add a pojo class for Contacts information named ContactObject.java

package com.multiselectlistexample;
public class ContactObject {
    
    private String contactName;
    private String contactNo;
    private String image;
    private boolean selected;
    public String getName() {
        return contactName;
    }
    public void setName(String contactName) {
        this.contactName = contactName;
    }
    public String getNumber() {
        return contactNo;
    }
    public void setNumber(String contactNo) {
        this.contactNo = contactNo;
    }
    public String getImage() {
        return image;
    }
    public void setImage(String image) {
        this.image = image;
    }
    public boolean isSelected() {
        return selected;
    }
    public void setSelected(boolean selected) {
        this.selected = selected;
    }
}

and add a arraylist to your project ContactsListClass.java

package com.multiselectlistexample;
import java.util.ArrayList;
public class ContactsListClass {
    public static final ArrayList<ContactObject> phoneList = new ArrayList<ContactObject>();
}

after that add a custom adapter class named ContactsAdapter.java. we will use this adaptor class as our multiple select listview’s adaptor.

package com.multiselectlistexample;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.ContactsContract.Contacts;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
public class ContactsAdapter extends BaseAdapter {
    Context mContext;
    LayoutInflater inflater;
    private List<ContactObject> mainDataList = null;
    private ArrayList<ContactObject> arraylist;
    public ContactsAdapter(Context context, List<ContactObject> mainDataList) {
    
        mContext = context;
        this.mainDataList = mainDataList;
        inflater = LayoutInflater.from(mContext);
        this.arraylist = new ArrayList<ContactObject>();
        this.arraylist.addAll(mainDataList);
        
        
    }
    static class ViewHolder {
        protected TextView name;
        protected TextView number;
        protected CheckBox check;
        protected ImageView image;
    }
    @Override
    public int getCount() {
        return mainDataList.size();
    }
    @Override
    public ContactObject getItem(int position) {
        return mainDataList.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    public View getView(final int position, View view, ViewGroup parent) {
        final ViewHolder holder;
        if (view == null) {
            holder = new ViewHolder();
            view = inflater.inflate(R.layout.list_row, null);
            holder.name = (TextView) view.findViewById(R.id.contactname);
            holder.number = (TextView) view.findViewById(R.id.contactno);
            holder.check = (CheckBox) view.findViewById(R.id.contactcheck);
            holder.image = (ImageView) view.findViewById(R.id.contactimage);
            view.setTag(holder);
            view.setTag(R.id.contactname, holder.name);
            view.setTag(R.id.contactno, holder.number);
            view.setTag(R.id.contactcheck, holder.check);
            holder.check
                    .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                        @Override
                        public void onCheckedChanged(CompoundButton vw,
                                boolean isChecked) {
                            int getPosition = (Integer) vw.getTag();
                            mainDataList.get(getPosition).setSelected(
                                    vw.isChecked());
                        }
                    });
        } else {
            holder = (ViewHolder) view.getTag();
        }
        holder.check.setTag(position);
        
        holder.name.setText(mainDataList.get(position).getName());
        holder.number.setText(mainDataList.get(position).getNumber());
        
        if(getByteContactPhoto(mainDataList.get(position).getImage())==null){
            holder.image.setImageResource(R.drawable.ic_launcher);
        }else{
            holder.image.setImageBitmap(getByteContactPhoto(mainDataList.get(position).getImage()));
        }
        
        
        
        holder.check.setChecked(mainDataList.get(position).isSelected());
        return view;
    }
    public void filter(String charText) {
        charText = charText.toLowerCase(Locale.getDefault());
        mainDataList.clear();
        if (charText.length() == 0) {
            mainDataList.addAll(arraylist);
        } else {
            for (ContactObject wp : arraylist) {
                if (wp.getName().toLowerCase(Locale.getDefault())
                        .contains(charText)) {
                    mainDataList.add(wp);
                }
            }
        }
        notifyDataSetChanged();
    }
    public Bitmap getByteContactPhoto(String contactId) {
        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.parseLong(contactId));
        Uri photoUri = Uri.withAppendedPath(contactUri, Contacts.Photo.CONTENT_DIRECTORY);
        Cursor cursor = mContext.getContentResolver().query(photoUri,
                        new String[] {Contacts.Photo.DATA15}, null, null, null);
        if (cursor == null) {
            return null;
        }
        try {
            if (cursor.moveToFirst()) {
                byte[] data = cursor.getBlob(0);
                if (data != null) {
                    return BitmapFactory.decodeStream( new ByteArrayInputStream(data));
                }
            }
        } finally {
            cursor.close();
        }
        return null;
        }
    
}

now made now use these classes in our activity like this.(eg.: MainActivity.java)

package com.multiselectlistexample;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;
public class MainActivity extends Activity {
    Context context = null;
    ContactsAdapter objAdapter;
    ListView lv = null;
    EditText edtSearch = null;
    LinearLayout llContainer = null;
    Button btnOK = null;
    RelativeLayout rlPBContainer = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context = this;
        setContentView(R.layout.activity_main);
        rlPBContainer = (RelativeLayout) findViewById(R.id.pbcontainer);
        edtSearch = (EditText) findViewById(R.id.input_search);
        llContainer = (LinearLayout) findViewById(R.id.data_container);
        btnOK = (Button) findViewById(R.id.ok_button);
        btnOK.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                getSelectedContacts();
            }
        });
        edtSearch.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence cs, int arg1, int arg2,
                    int arg3) {
                // When user changed the Text
                String text = edtSearch.getText().toString()
                        .toLowerCase(Locale.getDefault());
                objAdapter.filter(text);
            }
            @Override
            public void beforeTextChanged(CharSequence arg0, int arg1,
                    int arg2, int arg3) {
                // TODO Auto-generated method stub
            }
            @Override
            public void afterTextChanged(Editable arg0) {
                // TODO Auto-generated method stub
            }
        });
        addContactsInList();
    }
    private void getSelectedContacts() {
        // TODO Auto-generated method stub
        StringBuffer sb = new StringBuffer();
        for (ContactObject bean : ContactsListClass.phoneList) {
            if (bean.isSelected()) {
                sb.append(bean.getName());
                sb.append(",");
            }
        }
        String s = sb.toString().trim();
        if (TextUtils.isEmpty(s)) {
            Toast.makeText(context, "Select atleast one Contact",
                    Toast.LENGTH_SHORT).show();
        } else {
            s = s.substring(0, s.length() - 1);
            Toast.makeText(context, "Selected Contacts : " + s,
                    Toast.LENGTH_SHORT).show();
        }
    }
    private void addContactsInList() {
        // TODO Auto-generated method stub
        Thread thread = new Thread() {
            @Override
            public void run() {
                showPB();
                try {
                    Cursor phones = getContentResolver().query(
                            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                            null, null, null, null);
                    try {
                        ContactsListClass.phoneList.clear();
                    } catch (Exception e) {
                    }
                    while (phones.moveToNext()) {
                        String phoneName = phones
                                .getString(phones
                                        .getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                        String phoneNumber = phones
                                .getString(phones
                                        .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                        String phoneImage = phones
                                .getString(phones
                                        .getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
                        
                        ContactObject cp = new ContactObject();
                        
                        
                        cp.setName(phoneName);
                        cp.setNumber(phoneNumber);
                        cp.setImage(phoneImage);
                        ContactsListClass.phoneList.add(cp);
                    }
                    phones.close();
                    lv = new ListView(context);
                    lv.setLayoutParams(new LayoutParams(
                            LayoutParams.MATCH_PARENT,
                            LayoutParams.MATCH_PARENT));
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            // TODO Auto-generated method stub
                            llContainer.addView(lv);
                        }
                    });
                    Collections.sort(ContactsListClass.phoneList,
                            new Comparator<ContactObject>() {
                                @Override
                                public int compare(ContactObject lhs,
                                        ContactObject rhs) {
                                    return lhs.getName().compareTo(
                                            rhs.getName());
                                }
                            });
                    objAdapter = new ContactsAdapter(MainActivity.this,
                            ContactsListClass.phoneList);
                    lv.setAdapter(objAdapter);
                    lv.setOnItemClickListener(new OnItemClickListener() {
                        @Override
                        public void onItemClick(AdapterView<?> parent,
                                View view, int position, long id) {
                            CheckBox chk = (CheckBox) view
                                    .findViewById(R.id.contactcheck);
                            ContactObject bean = ContactsListClass.phoneList
                                    .get(position);
                            if (bean.isSelected()) {
                                bean.setSelected(false);
                                chk.setChecked(false);
                            } else {
                                bean.setSelected(true);
                                chk.setChecked(true);
                            }
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
                hidePB();
            }
        };
        thread.start();
    }
    void showPB() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                rlPBContainer.setVisibility(View.VISIBLE);
                edtSearch.setVisibility(View.GONE);
                btnOK.setVisibility(View.GONE);
            }
        });
    }
    void hidePB() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                rlPBContainer.setVisibility(View.GONE);
                edtSearch.setVisibility(View.VISIBLE);
                btnOK.setVisibility(View.VISIBLE);
            }
        });
    }
}

don’t forget to add permission READ_CONTACTS in your AndroidManifest.xml file like this.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.multiselectlistexample"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

now run this project you will see that it will show all phonebook contacts in listview, you can search a specific contact also using it’s search feature.

  • chief

    works nice and direct flow of explanations and thanks. however, y does it take long to load contacts. Can consideration be made for change in the while loop that seems to be looping indefinately

  • Ashok Agarwal

    Great work thanks

  • Chirag Patel

    headsup (y)

  • harish

    Hi,
    step1:i have selected on contact.
    step-2: i have searched for test contact then i have selected it.
    step3: now i have pressed on ok button .For me it is showing only test contact but there are two contacts i have selected how to get that contact also while retrieving.

    help me in solving it.

    Thanks
    Harish

  • Vikram Singh

    Thanks. I found best solution of my problem here. Thank u very much. Keep it up

  • Salman Khan

    All good but suppose first we select some contact then search and again select from the search data then click “ok” so it will show only contact selected after search not all. Please check it

    • deepakswami7

      hello salman, thanks for your comment, above code I used is for just a quick tip, it is not a full tutorial. I will try to improve it.

  • rozerin aktaş

    Hi! I am getting a nullpointer exception on filter method when I type anything on search screen. What can be the problem?

    • samir

      same as me

      • Hitesh Gupta

        This is because of MarshMallow update which requires permissions on runtime but code is not able to handle that.

        • samir

          then how to do this type of task…?

          • Hitesh Gupta

            Just to run this code, i changed all reference to contacts to point to my custom data

  • samir

    i need to download this example

  • TEJ

    how can i fetch selected contacts from this list view to another activity ….