19

My code for Spinner is below:

String[] countryNames = {"Select Country", "India", "China", "Australia",   "Portugle", "America", "New Zealand"};

Spinner spinner = (Spinner) findViewById(R.id.simpleSpinner);
hintAdapter = new CustomArrayAdapter(getApplicationContext(), R.layout.simple_row,countriesList,getApplicationContext());
spinner.setAdapter(hintAdapter);

I want to implement search in Spinner.

How can I achieve that?

earthw0rmjim
  • 19,027
  • 9
  • 49
  • 63
Sushant
  • 254
  • 1
  • 5
  • 15

5 Answers5

20

Use SearchableSpinner Lib, there is list of SearchableSpinner Library available just pick one of those which is better https://github.com/search?utf8=%E2%9C%93&q=searchable+spinner

Uttam Panchasara
  • 5,735
  • 5
  • 25
  • 41
5

Go for AutocompleteTextview this example will help you

Preetika Kaur
  • 1,991
  • 2
  • 16
  • 23
2

After trying a lot of libraries and method, I finally created my custom searchable spinner. The code I am attaching is at a very preliminary level which I will be updating as I do in my project. I am also writing the complete method of how to use it.

All Layouts searchable_spinner.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <EditText
        android:id="@+id/spinner_search_bar"
        android:layout_width="match_parent"
        android:layout_height="35dp"
        android:hint="Select A Company"
        android:cursorVisible="false"
        android:background="@drawable/white_rect_fillet_border"
        android:paddingHorizontal="10dp"/>
    
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/ledger_list"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:visibility="gone"
        android:layout_marginHorizontal="10dp"/>
</LinearLayout>
</FrameLayout>

actity_or_frag_layout.xml remember to include this as last and align according to your parent layout.

<FrameLayout
    android:id="@+id/spinner_frame"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginHorizontal="10dp"
    android:layout_marginTop="10dp"
    android:background="@drawable/white_rect_fillet_border"
    app:layout_constraintTop_toTopOf="parent">

    <include layout="@layout/searchable_spinner" />
</FrameLayout>

list_adapter_element.xml

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
<TableRow>
    <TextView
        android:id="@+id/txt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingVertical=5dp"
        android:gravity="center_vertical"/>
</TableRow>
</TableLayout>

All Drawables white_rect_fillet_border.xml

<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/white"/>
    <corners android:radius="5dp" />
    <stroke android:color="@android:color/darker_gray"
        android:width="1dp" />
</shape>

CustomListAdapter.java

public class CustomListAdapter extends 
RecyclerView.Adapter<CustomListAdapter.ViewHolder> {
private final Activity context;
private Fragment fragment;
private ArrayList<LedgerListObject> ledgerlist; //replace LedgerListObject with your object or simply String everywhere in this code.

public CustomListAdapter(Activity context, Fragment fragment, 
ArrayList<LedgerListObject> ledgerlist) {
    this.context = context;
    this.fragment = fragment;
    this.ledgerlist = ledgerlist;
}


public void updateList(ArrayList<LedgerListObject> newList){
    ledgerlist = newList;
    notifyDataSetChanged();
}

@NonNull
@Override
public CustomListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    LayoutInflater inflater = context.getLayoutInflater();
    View rowView= inflater.inflate(R.layout.list_adapter_element, null, true);
    return new ViewHolder(rowView);
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
    holder.txtTitle.setText(ledgerlist.get(position).LedgerName);
    holder.txtTitle.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            MyFragment.LedgerID = ledgerlist.get(position).LedgerID;  //MyFragment can be replaced with the name of your activity or fragment 
            MyFragment.ledgerListView.setVisibility(View.GONE);
            MyFragment.spinnerSearch.setText(ledgerlist.get(position).LedgerName);
            MyFragment.spinnerSearch.setCursorVisible(false);
        }
    });
}


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

class ViewHolder extends RecyclerView.ViewHolder {

    TextView txtTitle;
    ViewHolder(@NonNull View itemView) {
        super(itemView);
        txtTitle = (TextView) itemView.findViewById(R.id.txt);
    }
}
}

MyFragment.java

public class MyFragment extends Fragment{
ArrayList<LedgerListObject> ledgerlist = new ArrayList<LedgerListObject>();
public static int LedgerID = 0;
CustomListAdapter ledgerAdapter;

FrameLayout spinnerFrame;
public static EditText spinnerSearch;
public static RecyclerView ledgerListView;

 @SuppressLint("ClickableViewAccessibility")
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState){
    View root = inflater.inflate(R.layout.fragment_ledger, container, false);
    super.onCreate(savedInstanceState);
    spinnerFrame = root.findViewById(R.id.spinner_frame);
    spinnerSearch = root.findViewById(R.id.spinner_search_bar);
    ledgerListView = root.findViewById(R.id.ledger_list);
    spinnerSearch.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            ledgerListView.setVisibility(View.VISIBLE);
            spinnerSearch.setCursorVisible(true);
            return false;
        }
    });

    spinnerSearch.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) 
{

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            try{
                filter(s.toString());
            }catch (Exception e){e.printStackTrace();}
        }
    });

    GridLayoutManager listgridLayoutManager = new GridLayoutManager(getContext(), 1, 
    RecyclerView.VERTICAL, false);
    ledgerListView.setLayoutManager(listgridLayoutManager);

    //todo: your method of adding objects to your ledgerlist

    ledgerAdapter = new CustomListAdapter(getActivity(), LedgerFragment.this, ledgerlist);
            ledgerListView.setAdapter(ledgerAdapter);
    return root;
}
}

Try it and if there is any issue in this, please feel free to ask and I will resolve it

mohit48
  • 712
  • 7
  • 9
1

I found the following solution here:

/**
* A modified Spinner that doesn't automatically select the first entry in the list.
*
* Shows the prompt if nothing is selected.
*
* Limitations: does not display prompt if the entry list is empty.
*/
public class NoDefaultSpinner extends Spinner {

public NoDefaultSpinner(Context context) {
    super(context);
}
public NoDefaultSpinner(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public NoDefaultSpinner(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
public void setAdapter(SpinnerAdapter orig ) {
    final SpinnerAdapter adapter = newProxy(orig);

    super.setAdapter(adapter);

    try {
        final Method m = AdapterView.class.getDeclaredMethod(
                           "setNextSelectedPositionInt",int.class);
        m.setAccessible(true);
        m.invoke(this,-1);

        final Method n = AdapterView.class.getDeclaredMethod(
                           "setSelectedPositionInt",int.class);
        n.setAccessible(true);
        n.invoke(this,-1);
    } 
    catch( Exception e ) {
        throw new RuntimeException(e);
    }
}

protected SpinnerAdapter newProxy(SpinnerAdapter obj) {
    return (SpinnerAdapter) java.lang.reflect.Proxy.newProxyInstance(
            obj.getClass().getClassLoader(),
            new Class[]{SpinnerAdapter.class},
            new SpinnerAdapterProxy(obj));
}



/**
 * Intercepts getView() to display the prompt if position < 0
 */
protected class SpinnerAdapterProxy implements InvocationHandler {

    protected SpinnerAdapter obj;
    protected Method getView;


    protected SpinnerAdapterProxy(SpinnerAdapter obj) {
        this.obj = obj;
        try {
            this.getView = SpinnerAdapter.class.getMethod(
                             "getView",int.class,View.class,ViewGroup.class);
        } 
        catch( Exception e ) {
            throw new RuntimeException(e);
        }
    }

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        try {
            return m.equals(getView) && 
                   (Integer)(args[0])<0 ? 
                     getView((Integer)args[0],(View)args[1],(ViewGroup)args[2]) : 
                     m.invoke(obj, args);
        } 
        catch (InvocationTargetException e) {
            throw e.getTargetException();
        } 
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected View getView(int position, View convertView, ViewGroup parent) 
      throws IllegalAccessException {

        if( position<0 ) {
            final TextView v = 
              (TextView) ((LayoutInflater)getContext().getSystemService(
                Context.LAYOUT_INFLATER_SERVICE)).inflate(
                  android.R.layout.simple_spinner_item,parent,false);
            v.setText(getPrompt());
            return v;
        }
        return obj.getView(position,convertView,parent);
    }
}
}
LW001
  • 2,452
  • 6
  • 27
  • 36
1

You Can show Searchable Spinner in Dialog. here is all steps Searchable Spinner

Step 1: make a TextView inside activity_main.xml.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/text_view_for_dropdown"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:hint="Select Number"
        android:background="@android:drawable/editbox_background"
        app:drawableEndCompat="@drawable/ic_dropdown" />
    <!-- "@drawable/ic_dropdown" is "vector asset" of down arrow sign -->


</LinearLayout>

Step 2: make new layout (res>layout>layout_searchable_spinner.xml) that we will show inside Dialog.

layout_searchable_spinner.xml

<?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="match_parent"
    android:orientation="vertical"
    android:layout_margin="16dp">
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="25sp"
        android:fontFamily="monospace"
        android:text="Select Number"
        />
    <EditText
        android:id="@+id/editText_of_searchableSpinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Search..."
        android:padding="12dp"
        android:singleLine="true"
        android:background="@android:drawable/editbox_background"
        />
    <ListView
        android:id="@+id/listView_of_searchableSpinner"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        />

</LinearLayout>

Step 3: write all code in MainActivity.Java

MainActivity.Java

public class MainActivity extends AppCompatActivity {
    
    ArrayList<String> arrayList;
    TextView textViewSpinner;
    Dialog dialog;



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //provide size to arrayList
        arrayList=new ArrayList<>();
        //call this spinner function
        funCustomSpinner();
    }



    public void funCustomSpinner(){
        //we are adding values in arraylist
        arrayList.add("Item 1");
        arrayList.add("Item 2");
        arrayList.add("Item 3");
        arrayList.add("Item 4");
        arrayList.add("Item 4");
        arrayList.add("Item 4");

        //provide id to textview and set onClick lister
        textViewSpinner=findViewById(R.id.text_view_for_dropdown);
        textViewSpinner.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dialog=new Dialog(MainActivity.this);
                //set  (our custom layout for dialog)
                dialog.setContentView(R.layout.layout_searchable_spinner);

                //set transparent background
                dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.WHITE));
                //show dialog
                dialog.show();

                //initialize and assign variable
                EditText editText=dialog.findViewById(R.id.editText_of_searchableSpinner);
                ListView listView=dialog.findViewById(R.id.listView_of_searchableSpinner);
                //array adapter
                ArrayAdapter<String> arrayAdapter=new ArrayAdapter<>(MainActivity.this,
                        androidx.appcompat.R.layout.support_simple_spinner_dropdown_item,arrayList);
                listView.setAdapter(arrayAdapter);
                //Textwatcher for change data after every text type by user
                editText.addTextChangedListener(new TextWatcher() {
                    @Override
                    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                    }

                    @Override
                    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                        //filter arraylist
                        arrayAdapter.getFilter().filter(charSequence);
                    }
                    @Override
                    public void afterTextChanged(Editable editable) {
                    }
                });

                // listview onitem click listener
                listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                        textViewSpinner.setText(arrayAdapter.getItem(i));
                        Toast.makeText(MainActivity.this, "Selected:"+ arrayAdapter.getItem(i), Toast.LENGTH_SHORT).show();
                        //dismiss dialog after choose
                        dialog.dismiss();
                    }
                });
            }
        });

    }

Happy Coding:)

AG-Developer
  • 361
  • 2
  • 10