Thursday 22 March 2018

Multiple Selection Using Checkbox With Searchable ListView


Output :



 



colors.xml

<?xml version="1.0" encoding="utf-8"?><resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>

    <color name="list_even">#AAAAAA</color>
    <color name="list_odd">#E1E2E2</color>
    <color name="list_selected">#455FB6</color>
    <color name="red">#FF0000</color>

</resources>

strings.xml

<resources>
    <string name="app_name">MultipleSelectionCheckBox</string>

    <string name="type_to_search">Type to Search</string>

    <string-array name="sports_array">
        <item>Shuttle Badminton</item>
        <item>Tennis</item>
        <item>Table Tennis</item>
        <item>Basket Ball</item>
        <item>Foot Ball</item>
        <item>Volley Ball</item>
        <item>Hockey</item>
        <item>Swimming</item>

        <item>Shuttle Badminton 2</item>
        <item>Tennis 2</item>
        <item>Table Tennis 2</item>
        <item>Basket Ball 2</item>
        <item>Foot Ball 2</item>
        <item>Volley Ball 2</item>
        <item>Hockey 2</item>
        <item>Swimming 2</item>

        <item>Shuttle Badminton 3</item>
        <item>Tennis 3</item>
        <item>Table Tennis 3</item>
        <item>Basket Ball 3</item>
        <item>Foot Ball 3</item>
        <item>Volley Ball 3</item>
        <item>Hockey 3</item>
        <item>Swimming 3</item>

        <item>Shuttle Badminton 4</item>
        <item>Tennis 4</item>
        <item>Table Tennis 4</item>
        <item>Basket Ball 4</item>
        <item>Foot Ball 4</item>
        <item>Volley Ball 4</item>
        <item>Hockey 4</item>
        <item>Swimming 4</item>

        <item>Shuttle Badminton 5</item>
        <item>Tennis 5</item>
        <item>Table Tennis 5</item>
        <item>Basket Ball 5</item>
        <item>Foot Ball 5</item>
        <item>Volley Ball 5</item>
        <item>Hockey 5</item>
        <item>Swimming 5</item>
    </string-array>

</resources>

styles.xml

<resources>

    <!-- Base application theme. -->    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="myDialog" parent="@android:style/Theme.Holo.Light.Dialog">
        <item name="android:windowTitleStyle">@style/myDialogTitle</item>
    </style>

    <style name="myDialogTitle" parent="@android:style/TextAppearance.DialogWindowTitle">
        <item name="android:gravity">center_horizontal</item>
    </style>

    <style name="DialogWindowTitle">
        <item name="android:maxLines">1</item>
        <item name="android:scrollHorizontally">true</item>
        <item name="android:textAppearance">@android:style/TextAppearance.DialogWindowTitle</item>
    </style>

    <style name="LetterSectionListItem" parent="@android:style/TextAppearance.Medium">
        <item name="android:paddingLeft">5dp</item>
        <item name="android:textColor">#000000</item>
        <item name="android:background">#898989</item>
    </style>


</resources>


attrs.xml
<?xml version="1.0" encoding="utf-8"?><resources>
    <declare-styleable name="MultiSpinnerSearch">
        <attr name="hintText" format="string"/>
    </declare-styleable>
</resources>



KeyPairBoolData.java
package com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch;

public class KeyPairBoolData {

    long id;
    String name;
    boolean isSelected;

    Object object;

    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    /**     * @return the id     */    
    public long getId() {
        return id;
    }

    /**     * @param id the id to set     */    
    public void setId(long id) {
        this.id = id;
    }

    /**     * @return the name     */    
    public String getName() {
        return name;
    }

    /**     * @param name the name to set     */    
    public void setName(String name) {
        this.name = name;
    }

    /**     * @return the isSelected     */    
    public boolean isSelected() {
        return isSelected;
    }

    /**     * @param isSelected the isSelected to set     */    
    public void setSelected(boolean isSelected) {
        this.isSelected = isSelected;
    }
}


MultiSpinner.java
package com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.content.res.TypedArray;
import android.support.v7.widget.AppCompatSpinner;
import android.util.AttributeSet;
import android.widget.ArrayAdapter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

public class MultiSpinner extends AppCompatSpinner implements OnMultiChoiceClickListener, OnCancelListener {

    private List<String> items;
    private boolean[] selected;
    private String defaultText = "Select Items";
    private String spinnerTitle = "";
    private MultiSpinnerListener listener;

    public MultiSpinner(Context context) {
        super(context);
    }

    public MultiSpinner(Context arg0, AttributeSet arg1) {
        super(arg0, arg1);
        TypedArray a = arg0.obtainStyledAttributes(arg1, R.styleable.MultiSpinnerSearch);
        final int N = a.getIndexCount();
        for (int i = 0; i < N; ++i) {
            int attr = a.getIndex(i);
            if (attr == R.styleable.MultiSpinnerSearch_hintText) {
                spinnerTitle = a.getString(attr);
            }
        }
        a.recycle();
    }

    public MultiSpinner(Context arg0, AttributeSet arg1, int arg2) {
        super(arg0, arg1, arg2);
    }

    @Override    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
        selected[which] = isChecked;
    }

    @Override    public void onCancel(DialogInterface dialog) {
        // refresh text on spinner        
        StringBuilder spinnerBuffer = new StringBuilder();
        for (int i = 0; i < items.size(); i++) {
            if (selected[i]) {
                spinnerBuffer.append(items.get(i));
                spinnerBuffer.append(", ");
            }
        }

        String spinnerText = spinnerBuffer.toString();
        if (spinnerText.length() > 2) {
            spinnerText = spinnerText.substring(0, spinnerText.length() - 2);
        } else {
            spinnerText = defaultText;
        }

        ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(), R.layout.textview_for_spinner, new String[]{spinnerText});
        setAdapter(adapter);
        if (selected.length > 0) {
            listener.onItemsSelected(selected);
        }

    }

    @Override    public boolean performClick() {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), R.style.myDialog);
        builder.setTitle(spinnerTitle);
        builder.setMultiChoiceItems(
                items.toArray(new CharSequence[items.size()]), selected, this);
        builder.setPositiveButton(android.R.string.ok,
                new DialogInterface.OnClickListener() {

                    @Override                    
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
        builder.setOnCancelListener(this);
        builder.show();
        return true;
    }

    /**     * Sets items to this spinner.     *     
            * @param items    
            A TreeMap where the keys are the values to display in the spinner     * 
            and the value the initial selected state of the key.     * 
            @param listener A MultiSpinnerListener.     */    
        public void setItems(LinkedHashMap<String, Boolean> items, MultiSpinnerListener listener) {
        this.items = new ArrayList<>(items.keySet());
        this.listener = listener;

        List<Boolean> values = new ArrayList<>(items.values());
        selected = new boolean[values.size()];
        for (int i = 0; i < items.size(); i++) {
            selected[i] = values.get(i);
        }

        // all text on the spinner        
        ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(), R.layout.textview_for_spinner, new String[]{defaultText});
        setAdapter(adapter);

        // Set Spinner Text        
        onCancel(null);
    }

}



MultiSpinnerListener.java
package com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch;

public interface MultiSpinnerListener {
    void onItemsSelected(boolean[] selected);
}




MultiSpinnerSearch.java
package com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch;

import android.annotation.SuppressLint;
import android.app.SearchManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Typeface;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.AppCompatSpinner;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;

public class MultiSpinnerSearch extends AppCompatSpinner implements DialogInterface.OnCancelListener {
    private static final String TAG = MultiSpinnerSearch.class.getSimpleName();
    private List<KeyPairBoolData> items;
    private String defaultText = "";
    private String spinnerTitle = "";
    private SpinnerListener listener;
    private int limit = -1;
    private int selected = 0;
    private LimitExceedListener limitListener;
    MyAdapter adapter;
    public static AlertDialog.Builder builder;
    public static AlertDialog ad;

    public MultiSpinnerSearch(Context context) {
        super(context);
    }

    public MultiSpinnerSearch(Context arg0, AttributeSet arg1) {
        super(arg0, arg1);
        TypedArray a = arg0.obtainStyledAttributes(arg1, R.styleable.MultiSpinnerSearch);
        for (int i = 0; i < a.getIndexCount(); ++i) {
            int attr = a.getIndex(i);
            if (attr == R.styleable.MultiSpinnerSearch_hintText) {
                spinnerTitle = a.getString(attr);
                defaultText = spinnerTitle;
                break;
            }
        }
        Log.i(TAG, "spinnerTitle: "+spinnerTitle);
        a.recycle();
    }

    public MultiSpinnerSearch(Context arg0, AttributeSet arg1, int arg2) {
        super(arg0, arg1, arg2);
    }

    public void setLimit(int limit, LimitExceedListener listener) {
        this.limit = limit;
        this.limitListener = listener;
    }

    public List<KeyPairBoolData> getSelectedItems() {
        List<KeyPairBoolData> selectedItems = new ArrayList<>();
        for(KeyPairBoolData item : items){
            if(item.isSelected()){
                selectedItems.add(item);
            }
        }
        return selectedItems;
    }

    public List<Long> getSelectedIds() {
        List<Long> selectedItemsIds = new ArrayList<>();
        for(KeyPairBoolData item : items){
            if(item.isSelected()){
                selectedItemsIds.add(item.getId());
            }
        }
        return selectedItemsIds;
    }

    @Override    public void onCancel(DialogInterface dialog) {
        // refresh text on spinner
        StringBuilder spinnerBuffer = new StringBuilder();

        for (int i = 0; i < items.size(); i++) {
            if (items.get(i).isSelected()) {
                spinnerBuffer.append(items.get(i).getName());
                spinnerBuffer.append(", ");
            }
        }

        String spinnerText = spinnerBuffer.toString();
        if (spinnerText.length() > 2)
            spinnerText = spinnerText.substring(0, spinnerText.length() - 2);
        else            spinnerText = defaultText;

        ArrayAdapter<String> adapterSpinner = new ArrayAdapter<>(getContext(), R.layout.textview_for_spinner, new String[]{spinnerText});
        setAdapter(adapterSpinner);

        if (adapter != null)
            adapter.notifyDataSetChanged();

        listener.onItemsSelected(items);
    }

    @Override    public boolean performClick() {

//        builder = new AlertDialog.Builder(getContext(), R.style.myDialog);        
        builder = new AlertDialog.Builder(getContext());
        builder.setTitle(spinnerTitle);

        final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        final View view = inflater.inflate(R.layout.alert_dialog_listview_search, null);
        builder.setView(view);

        final ListView listView = (ListView) view.findViewById(R.id.alertSearchListView);
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
        listView.setFastScrollEnabled(false);
        adapter = new MyAdapter(getContext(), items);
        listView.setAdapter(adapter);

        final TextView emptyText = (TextView) view.findViewById(R.id.empty);
        listView.setEmptyView(emptyText);

        final EditText editText = (EditText) view.findViewById(R.id.alertSearchEditText);
        editText.addTextChangedListener(new TextWatcher() {

            @Override            
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                adapter.getFilter().filter(s.toString());
            }

            @Override            
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override            
            public void afterTextChanged(Editable s) {
            }
        });

        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {

            @Override            public void onClick(DialogInterface dialog, int which) {

                Log.i(TAG, " ITEMS : " + items.size());
                dialog.cancel();
            }
        });

        builder.setOnCancelListener(this);
        ad = builder.show();
        return true;
    }

    public void setItems(List<KeyPairBoolData> items, int position, SpinnerListener listener) {

        this.items = items;
        this.listener = listener;

        StringBuilder spinnerBuffer = new StringBuilder();

        for (int i = 0; i < items.size(); i++) {
            if (items.get(i).isSelected()) {
                spinnerBuffer.append(items.get(i).getName());
                spinnerBuffer.append(", ");
            }
        }
        if (spinnerBuffer.length() > 2)
            defaultText = spinnerBuffer.toString().substring(0, spinnerBuffer.toString().length() - 2);

        ArrayAdapter<String> adapterSpinner = new ArrayAdapter<>(getContext(), R.layout.textview_for_spinner, new String[]{defaultText});
        setAdapter(adapterSpinner);

        if (position != -1) {
            items.get(position).setSelected(true);
            //listener.onItemsSelected(items);            
            onCancel(null);
        }
    }

    public interface LimitExceedListener {
        void onLimitListener(KeyPairBoolData data);
    }

    //Adapter Class    public class MyAdapter extends BaseAdapter implements Filterable {

        List<KeyPairBoolData> arrayList;
        List<KeyPairBoolData> mOriginalValues; 
        // Original Values        
        LayoutInflater inflater;

        public MyAdapter(Context context, List<KeyPairBoolData> arrayList) {
            this.arrayList = arrayList;
            inflater = LayoutInflater.from(context);
        }

        @Override        public int getCount() {
            return arrayList.size();
        }

        @Override        public Object getItem(int position) {
            return position;
        }

        @Override        public long getItemId(int position) {
            return position;
        }

        private class ViewHolder {
            TextView textView;
            CheckBox checkBox;
        }

        @Override        
         public View getView(final int position, View convertView, ViewGroup parent) {
            Log.i(TAG, "getView() enter");
            ViewHolder holder;

            if (convertView == null) {
                holder = new ViewHolder();
                convertView = inflater.inflate(R.layout.item_listview_multiple,  parent, false);
                holder.textView = (TextView) convertView.findViewById(R.id.alertTextView);
                holder.checkBox = (CheckBox) convertView.findViewById(R.id.alertCheckbox);

                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }

            final int backgroundColor = (position%2 == 0) ? R.color.list_even : R.color.list_odd;
            convertView.setBackgroundColor(ContextCompat.getColor(getContext(), backgroundColor));

            final KeyPairBoolData data = arrayList.get(position);

            holder.textView.setText(data.getName());
            holder.textView.setTypeface(null, Typeface.NORMAL);
            holder.checkBox.setChecked(data.isSelected());

            convertView.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    if(data.isSelected()) { 
                      // deselect          
                     selected--;
                    } else if(selected == limit) { 
                        // select with limit     
                        if(limitListener != null)
                        limitListener.onLimitListener(data);
                        return;
                    } else { 
                        // selected
                        selected++;
                    }

                    final ViewHolder temp = (ViewHolder) v.getTag();
                    temp.checkBox.setChecked(!temp.checkBox.isChecked());

                    data.setSelected(!data.isSelected());
                    Log.i(TAG, "On Click Selected Item : " + data.getName() + " : " + data.isSelected());
                    notifyDataSetChanged();
                }
            });
            if (data.isSelected()) {
                holder.textView.setTypeface(null, Typeface.BOLD);
                holder.textView.setTextColor(Color.WHITE);
                convertView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.list_selected));
            }
            holder.checkBox.setTag(holder);

            return convertView;
        }

        @SuppressLint("DefaultLocale")
        @Override        public Filter getFilter() {
            return new Filter() {

                @SuppressWarnings("unchecked")
                @Override
                protected void publishResults(CharSequence constraint, FilterResults results) {

                    arrayList = (List<KeyPairBoolData>) results.values;
                     // has the filtered values
                    notifyDataSetChanged();
                    // notifies the data with new filtered values 
               }

                @Override
                protected FilterResults performFiltering(CharSequence constraint) {
                    FilterResults results = new FilterResults();
                    // Holds the results of a filtering operation in values
                    List<KeyPairBoolData> FilteredArrList = new ArrayList<>();

                    if (mOriginalValues == null) {
                        mOriginalValues = new ArrayList<>(arrayList);
                        // saves the original data in mOriginalValues
                    }

                    /********
                     *                     
                     *  If constraint(CharSequence that is received) is null returns the mOriginalValues(Original) values
                     *  else does the Filtering and returns FilteredArrList(Filtered)
                     *                     ********/
                    if (constraint == null || constraint.length() == 0) {

                        // set the Original result to return
                        results.count = mOriginalValues.size();
                        results.values = mOriginalValues;
                    } else {
                        constraint = constraint.toString().toLowerCase();
                        for (int i = 0; i < mOriginalValues.size(); i++) {
                            Log.i(TAG, "Filter : " + mOriginalValues.get(i).getName() + " -> " + mOriginalValues.get(i).isSelected());
                            String data = mOriginalValues.get(i).getName();
                            if (data.toLowerCase().contains(constraint.toString())) {
                                FilteredArrList.add(mOriginalValues.get(i));
                            }
                        }
                        // set the Filtered result to return
                        results.count = FilteredArrList.size();
                        results.values = FilteredArrList;
                    }
                    return results;
                }
            };
        }
    }
}




SingleSpinner.java
package com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch;

import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Typeface;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;

public class SingleSpinner extends android.support.v7.widget.AppCompatSpinner implements OnCancelListener {
   private static final String TAG = SingleSpinner.class.getSimpleName();
   private List<KeyPairBoolData> items;
   private String defaultText = "";
   private String spinnerTitle = "";
   private SpinnerListener listener;
   MyAdapter adapter;
   public static AlertDialog.Builder builder;
   public static AlertDialog ad;

   public SingleSpinner(Context context) {
      super(context);
   }

   public SingleSpinner(Context arg0, AttributeSet arg1) {
      super(arg0, arg1);
      TypedArray a = arg0.obtainStyledAttributes(arg1, R.styleable.MultiSpinnerSearch);
      final int N = a.getIndexCount();
      for (int i = 0; i < N; ++i) {
         int attr = a.getIndex(i);
         if (attr == R.styleable.MultiSpinnerSearch_hintText) {
            spinnerTitle = a.getString(attr);
            defaultText = spinnerTitle;
            break;
         }
      }
      Log.i(TAG, "spinnerTitle: "+spinnerTitle);
      a.recycle();
   }

   public SingleSpinner(Context arg0, AttributeSet arg1, int arg2) {
      super(arg0, arg1, arg2);
   }


   public List<KeyPairBoolData> getSelectedItems() {
      List<KeyPairBoolData> selectedItems = new ArrayList<>();
      for(KeyPairBoolData item : items){
         if(item.isSelected()){
            selectedItems.add(item);
         }
      }
      return selectedItems;
   }

   public List<Long> getSelectedIds() {
      List<Long> selectedItemsIds = new ArrayList<>();
      for(KeyPairBoolData item : items){
         if(item.isSelected()){
            selectedItemsIds.add(item.getId());
         }
      }
      return selectedItemsIds;
   }

   @Override   public void onCancel(DialogInterface dialog) {
      // refresh text on spinner
      String spinnerText = null;
      for (int i = 0; i < items.size(); i++) {
         if (items.get(i).isSelected()) {
            spinnerText = items.get(i).getName();
            break;
         }
      }
      if (spinnerText==null){
         spinnerText = defaultText;
      }

      ArrayAdapter<String> adapterSpinner = new ArrayAdapter<>(getContext(), R.layout.textview_for_spinner, new String[] { spinnerText });
      setAdapter(adapterSpinner);

      if(adapter != null)
         adapter.notifyDataSetChanged();

      listener.onItemsSelected(items);
   }

   @Override   public boolean performClick() {

//    builder = new AlertDialog.Builder(getContext(), R.style.myDialog);
      builder = new AlertDialog.Builder(getContext());
      builder.setTitle(spinnerTitle);

      LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE );

      View view = inflater.inflate(R.layout.alert_dialog_listview_search, null);
      builder.setView(view);

      final ListView listView = (ListView) view.findViewById(R.id.alertSearchListView);
      listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
      listView.setFastScrollEnabled(false);
      adapter = new MyAdapter(getContext(), items);

      listView.setAdapter(adapter);
      final TextView emptyText = (TextView) view.findViewById(R.id.empty);
      listView.setEmptyView(emptyText);

      EditText editText = (EditText) view.findViewById(R.id.alertSearchEditText);
      editText.setVisibility(GONE);

      builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {

         @Override
         public void onClick(DialogInterface dialog, int which) {

            Log.i(TAG, " ITEMS : " + items.size() );
            dialog.cancel();
         }
      });

      builder.setOnCancelListener(this);
      ad = builder.show();
      return true;
   }

   public void setItems(List<KeyPairBoolData> items, int position, SpinnerListener listener) {

      this.items = items;
      this.listener = listener;

      for (KeyPairBoolData item : items){
         if(item.isSelected()){
            defaultText = item.getName();
            break;
         }
      }

      ArrayAdapter<String> adapterSpinner = new ArrayAdapter<>(getContext(), R.layout.textview_for_spinner, new String[] { defaultText });
      setAdapter(adapterSpinner);

      if(position != -1)
      {
         items.get(position).setSelected(true);
         //listener.onItemsSelected(items);
         onCancel(null);
      }
   }



   //Adapter Class
   public class MyAdapter extends BaseAdapter{

      List<KeyPairBoolData> arrayList;
      LayoutInflater inflater;

      public MyAdapter(Context context, List<KeyPairBoolData> arrayList) {
         this.arrayList = arrayList;
         inflater = LayoutInflater.from(context);
      }

      @Override      public int getCount() {
         return arrayList.size();
      }

      @Override      public Object getItem(int position) {
         return position;
      }

      @Override      public long getItemId(int position) {
         return position;
      }

      private class ViewHolder {
         TextView textView;
      }

      @Override      public View getView(final int position, View convertView, ViewGroup parent) {
         Log.i(TAG, "getView() enter");
         ViewHolder holder = new ViewHolder();
         convertView = inflater.inflate(R.layout.item_listview_single,  parent, false);
         holder.textView = (TextView) convertView.findViewById(R.id.alertTextView);
         convertView.setTag(holder);

         if(position%2==0){
            convertView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.list_even));
         }else{
            convertView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.list_odd));
         }

         final KeyPairBoolData data = arrayList.get(position);

         holder.textView.setText(data.getName());
         holder.textView.setTypeface(null, Typeface.NORMAL);

         convertView.setOnClickListener(new OnClickListener()
         {
            public void onClick(View v)
            {
               int len = arrayList.size();
               for (int i = 0; i < len; i++)
               {
                  arrayList.get(i).setSelected(false);
                  if (i == position)
                  {
                     arrayList.get(i).setSelected(true);
                     Log.i(TAG, "On Click Selected Item : " + arrayList.get(i).getName() + " : " + arrayList.get(i).isSelected());
                  }
               }
               ad.dismiss();
               SingleSpinner.this.onCancel(ad);
            }
         });

         if(data.isSelected()){
            holder.textView.setTypeface(null, Typeface.BOLD);
            holder.textView.setTextColor(Color.WHITE);
            convertView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.list_selected));
         }
         return convertView;
      }
   }
}




SingleSpinnerSearch.java
package com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch;

import android.annotation.SuppressLint;
import android.support.v7.app.AlertDialog;
//import android.app.AlertDialog;import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Typeface;
import android.support.v4.content.ContextCompat;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;

public class SingleSpinnerSearch extends android.support.v7.widget.AppCompatSpinner implements OnCancelListener {
   private static final String TAG = SingleSpinnerSearch.class.getSimpleName();
   private List<KeyPairBoolData> items;
   private String defaultText = "";
   private String spinnerTitle = "";
   private SpinnerListener listener;
   MyAdapter adapter;
   public static AlertDialog.Builder builder;
   public static AlertDialog ad;

   public SingleSpinnerSearch(Context context) {
      super(context);
   }

   public SingleSpinnerSearch(Context arg0, AttributeSet arg1) {
      super(arg0, arg1);
      TypedArray a = arg0.obtainStyledAttributes(arg1, R.styleable.MultiSpinnerSearch);
      final int N = a.getIndexCount();
      for (int i = 0; i < N; ++i) {
         int attr = a.getIndex(i);
         if (attr == R.styleable.MultiSpinnerSearch_hintText) {
            spinnerTitle = a.getString(attr);
            defaultText = spinnerTitle;
            break;
         }
      }
      Log.i(TAG, "spinnerTitle: "+spinnerTitle);
      a.recycle();
   }

   public SingleSpinnerSearch(Context arg0, AttributeSet arg1, int arg2) {
      super(arg0, arg1, arg2);
   }


   public List<KeyPairBoolData> getSelectedItems() {
      List<KeyPairBoolData> selectedItems = new ArrayList<>();
      for(KeyPairBoolData item : items){
         if(item.isSelected()){
            selectedItems.add(item);
         }
      }
      return selectedItems;
   }

   public List<Long> getSelectedIds() {
      List<Long> selectedItemsIds = new ArrayList<>();
      for(KeyPairBoolData item : items){
         if(item.isSelected()){
            selectedItemsIds.add(item.getId());
         }
      }
      return selectedItemsIds;
   }

   @Override   public void onCancel(DialogInterface dialog) {
      // refresh text on spinner
      String spinnerText = null;
      for (int i = 0; i < items.size(); i++) {
         if (items.get(i).isSelected()) {
            spinnerText = items.get(i).getName();
            break;
         }
      }
      if (spinnerText==null){
         spinnerText = defaultText;
      }

      ArrayAdapter<String> adapterSpinner = new ArrayAdapter<>(getContext(), R.layout.textview_for_spinner, new String[] { spinnerText });
      setAdapter(adapterSpinner);

      if(adapter != null)
         adapter.notifyDataSetChanged();

      listener.onItemsSelected(items);
   }

   @Override   public boolean performClick() {

//    builder = new AlertDialog.Builder(getContext(), R.style.myDialog);
      builder = new AlertDialog.Builder(getContext());
      builder.setTitle(spinnerTitle);

      final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE );

      final View view = inflater.inflate(R.layout.alert_dialog_listview_search, null);
      builder.setView(view);

      final ListView listView = (ListView) view.findViewById(R.id.alertSearchListView);
      listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
      listView.setFastScrollEnabled(false);
      adapter = new MyAdapter(getContext(), items);

      listView.setAdapter(adapter);
      for(int i =0;i<items.size();i++){
         if(items.get(i).isSelected()){
            listView.setSelection(i);
            break;
         }
      }
      final TextView emptyText = (TextView) view.findViewById(R.id.empty);
      listView.setEmptyView(emptyText);

      EditText editText = (EditText) view.findViewById(R.id.alertSearchEditText);
      editText.addTextChangedListener(new TextWatcher() {

         @Override
         public void onTextChanged(CharSequence s, int start, int before, int count) {
            adapter.getFilter().filter(s.toString());
         }

         @Override
         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
         }

         @Override
         public void afterTextChanged(Editable s) {
         }
      });

      builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {

         @Override         public void onClick(DialogInterface dialog, int which) {

            Log.i(TAG, " ITEMS : " + items.size() );
            dialog.cancel();
         }
      });

      builder.setOnCancelListener(this);
      ad = builder.show();
      return true;
   }

   public void setItems(List<KeyPairBoolData> items, int position, SpinnerListener listener) {

      this.items = items;
      this.listener = listener;

      for (KeyPairBoolData item : items){
         if(item.isSelected()){
            defaultText = item.getName();
            break;
         }
      }

      ArrayAdapter<String> adapterSpinner = new ArrayAdapter<>(getContext(), R.layout.textview_for_spinner, new String[] { defaultText });
      setAdapter(adapterSpinner);

      if(position != -1)
      {
         items.get(position).setSelected(true);
         //listener.onItemsSelected(items);
         onCancel(null);
      }
   }



   //Adapter Class
   public class MyAdapter extends BaseAdapter implements Filterable {

      List<KeyPairBoolData> arrayList;
      List<KeyPairBoolData> mOriginalValues;
      // Original Values
      LayoutInflater inflater;

      public MyAdapter(Context context, List<KeyPairBoolData> arrayList) {
         this.arrayList = arrayList;
         inflater = LayoutInflater.from(context);
      }

      @Override      public int getCount() {
         return arrayList.size();
      }

      @Override      public Object getItem(int position) {
         return position;
      }

      @Override      public long getItemId(int position) {
         return position;
      }

      private class ViewHolder {
         TextView textView;
      }

      @Override      public View getView(final int position, View convertView, ViewGroup parent) {
         Log.i(TAG, "getView() enter");
         ViewHolder holder = new ViewHolder();
         convertView = inflater.inflate(R.layout.item_listview_single, parent, false);
         holder.textView = (TextView) convertView.findViewById(R.id.alertTextView);
         convertView.setTag(holder);

         if(position%2==0){
            convertView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.list_even));
         }else{
            convertView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.list_odd));
         }

         final KeyPairBoolData data = arrayList.get(position);

         holder.textView.setText(data.getName());
         holder.textView.setTypeface(null, Typeface.NORMAL);

         convertView.setOnClickListener(new OnClickListener()
         {
            public void onClick(View v)
            {
               int len = arrayList.size();
               for (int i = 0; i < len; i++)
               {
                  arrayList.get(i).setSelected(false);
                  if (i == position)
                  {
                     arrayList.get(i).setSelected(true);
                     Log.i(TAG, "On Click Selected Item : " + arrayList.get(i).getName() + " : " + arrayList.get(i).isSelected());
                  }
               }
               ad.dismiss();
               SingleSpinnerSearch.this.onCancel(ad);
            }
         });

         if(data.isSelected()){
            holder.textView.setTypeface(null, Typeface.BOLD);
            holder.textView.setTextColor(Color.WHITE);
            convertView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.list_selected));
         }
         return convertView;
      }

      @SuppressLint("DefaultLocale")
      @Override      public Filter getFilter() {
         return new Filter() {

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint,FilterResults results) {

               arrayList = (List<KeyPairBoolData>) results.values;
               // has the filtered values
               notifyDataSetChanged();  // notifies the data with new filtered values            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
               FilterResults results = new FilterResults();
               // Holds the results of a filtering operation in values
               List<KeyPairBoolData> FilteredArrList = new ArrayList<>();

               if (mOriginalValues == null) {
                  mOriginalValues = new ArrayList<>(arrayList);
                // saves the original data in mOriginalValues               }

               /********
                *
                 *  If constraint(CharSequence that is received) is null returns the mOriginalValues(Original) values
                *  else does the Filtering and returns FilteredArrList(Filtered)
                  *                ********/
               if (constraint == null || constraint.length() == 0) {

                  // set the Original result to return
                    results.count = mOriginalValues.size();
                  results.values = mOriginalValues;
               } else {
                  constraint = constraint.toString().toLowerCase();
                  for (int i = 0; i < mOriginalValues.size(); i++) {
                     Log.i(TAG, "Filter : " + mOriginalValues.get(i).getName() + " -> " + mOriginalValues.get(i).isSelected());
                     String data = mOriginalValues.get(i).getName();
                     if (data.toLowerCase().contains(constraint.toString())) {
                        FilteredArrList.add(mOriginalValues.get(i));
                     }
                  }
                  // set the Filtered result to return
                  results.count = FilteredArrList.size();
                  results.values = FilteredArrList;
               }
               return results;
            }
         };
      }
   }
}




SpinnerListener.java
package com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch;

import java.util.List;

public interface SpinnerListener {
    void onItemsSelected(List<KeyPairBoolData> items);
}




activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:layout_marginBottom="10dp"    android:orientation="vertical">

    <com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch.MultiSpinnerSearch        android:id="@+id/searchMultiSpinnerUnlimited"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_margin="10dp"        app:hintText="Multi Selection With Filter Without Limit" />

    <com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch.MultiSpinnerSearch        android:id="@+id/searchMultiSpinnerLimit"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_margin="10dp"        app:hintText="Multi Selection With Filter And Limit" />

    <com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch.SingleSpinnerSearch        android:id="@+id/searchSingleSpinner"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_margin="10dp"        app:hintText="Single Selection With Filter" />

    <com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch.SingleSpinner        android:id="@+id/singleSpinner"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_margin="10dp"        app:hintText="Single Selection" />

</LinearLayout>
MainActivity.java
package com.jainishprajapati.multiselectcheckboxwithsearchandwithoutsearch;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

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

        /**         * Getting array of String to Bind in Spinner         */
        final List<String> list = Arrays.asList(getResources().getStringArray(R.array.sports_array));

        final List<KeyPairBoolData> listArray0 = new ArrayList<>();

        for (int i = 0; i < list.size(); i++) {
            KeyPairBoolData h = new KeyPairBoolData();
            h.setId(i + 1);
            h.setName(list.get(i));
            h.setSelected(false);
            listArray0.add(h);
        }

        final List<KeyPairBoolData> listArray1 = new ArrayList<>();

        for (int i = 0; i < list.size(); i++) {
            KeyPairBoolData h = new KeyPairBoolData();
            h.setId(i + 1);
            h.setName(list.get(i));
            h.setSelected(false);
            listArray1.add(h);
        }

        final List<KeyPairBoolData> listArray2 = new ArrayList<>();

        for (int i = 0; i < list.size(); i++) {
            KeyPairBoolData h = new KeyPairBoolData();
            h.setId(i + 1);
            h.setName(list.get(i));
            h.setSelected(false);
            listArray2.add(h);
        }
        final List<KeyPairBoolData> listArray3 = new ArrayList<>();

        for (int i = 0; i < list.size(); i++) {
            KeyPairBoolData h = new KeyPairBoolData();
            h.setId(i + 1);
            h.setName(list.get(i));
            h.setSelected(false);
            listArray3.add(h);
        }    
        
        MultiSpinnerSearch searchMultiSpinnerUnlimited = (MultiSpinnerSearch) findViewById(R.id.searchMultiSpinnerUnlimited);
        MultiSpinnerSearch searchMultiSpinnerLimit = (MultiSpinnerSearch) findViewById(R.id.searchMultiSpinnerLimit);
        SingleSpinnerSearch searchSingleSpinner = (SingleSpinnerSearch) findViewById(R.id.searchSingleSpinner);
        SingleSpinner singleSpinner = (SingleSpinner) findViewById(R.id.singleSpinner);

        searchMultiSpinnerUnlimited.setItems(listArray0, -1, new SpinnerListener() {

            @Override            public void onItemsSelected(List<KeyPairBoolData> items) {

                for (int i = 0; i < items.size(); i++) {
                    if (items.get(i).isSelected()) {
                        Log.i(TAG, i + " : " + items.get(i).getName() + " : " + items.get(i).isSelected());
                    }
                }
            }
        });

        /***         * -1 is no by default selection         * 0 to length will select corresponding values         */        searchMultiSpinnerLimit.setItems(listArray1, -1, new SpinnerListener() {

            @Override            public void onItemsSelected(List<KeyPairBoolData> items) {

                for (int i = 0; i < items.size(); i++) {
                    if (items.get(i).isSelected()) {
                        Log.i(TAG, i + " : " + items.get(i).getName() + " : " + items.get(i).isSelected());
                    }
                }
            }
        });

        searchMultiSpinnerLimit.setLimit(2, new MultiSpinnerSearch.LimitExceedListener() {
            @Override            public void onLimitListener(KeyPairBoolData data) {
                Toast.makeText(getApplicationContext(),
                        "Limit exceed ", Toast.LENGTH_LONG).show();
            }
        });

        searchSingleSpinner.setItems(listArray2, -1, new SpinnerListener() {

            @Override            public void onItemsSelected(List<KeyPairBoolData> items) {

                for (int i = 0; i < items.size(); i++) {
                    if (items.get(i).isSelected()) {
                        Log.i(TAG, i + " : " + items.get(i).getName() + " : " + items.get(i).isSelected());
                    }
                }
            }
        });

        singleSpinner.setItems(listArray3, -1, new SpinnerListener() {

            @Override            public void onItemsSelected(List<KeyPairBoolData> items) {

                for (int i = 0; i < items.size(); i++) {
                    if (items.get(i).isSelected()) {
                        Log.i(TAG, i + " : " + items.get(i).getName() + " : " + items.get(i).isSelected());
                    }
                }
            }
        });
    }
}