83

I need to create SearchView from my arrayList<String> and show the suggestions in the drop-down list same this

enter image description here

I look for tutorials that explain step by step how to build a SearchView in a action bar.

I have read the documentation and following the example google but it was not useful to me.

I have created the search

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/action_search"
          android:title="Search"
          android:icon="@android:drawable/ic_menu_search"
          android:showAsAction="always"
          android:actionViewClass="android.widget.SearchView" />
</menu>`

But I do not know how to set the parameters of the array of strings. I tried to retrieve the result in a different Activity but not work.

Vini.g.fer
  • 11,639
  • 16
  • 61
  • 90
Matteo
  • 1,241
  • 2
  • 21
  • 28
  • 6
    You should post everything you've tried, in as much detail as possible. Specific questions with sample code tend to get faster, more meaningful answers. – Collin Flynn Feb 05 '14 at 18:26

4 Answers4

128

It took a while to put together a solution for this, but have found this is the easiest way to get it to work in the way that you describe. There could be better ways to do this, but since you haven't posted your activity code I will have to improvise and assume you have a list like this at the start of your activity:

private List<String> items = db.getItems();

ExampleActivity.java

private List<String> items;

private Menu menu;

@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public boolean onCreateOptionsMenu(Menu menu) {

    getMenuInflater().inflate(R.menu.example, menu);

    this.menu = menu;

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

        SearchManager manager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);

        SearchView search = (SearchView) menu.findItem(R.id.search).getActionView();

        search.setSearchableInfo(manager.getSearchableInfo(getComponentName()));

        search.setOnQueryTextListener(new OnQueryTextListener() { 

            @Override 
            public boolean onQueryTextChange(String query) {

                loadHistory(query);

                return true; 

            } 

        });

    }

    return true;

}

// History
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void loadHistory(String query) {

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

        // Cursor
        String[] columns = new String[] { "_id", "text" };
        Object[] temp = new Object[] { 0, "default" };

        MatrixCursor cursor = new MatrixCursor(columns);

        for(int i = 0; i < items.size(); i++) {

            temp[0] = i;
            temp[1] = items.get(i);

            cursor.addRow(temp);

        }

        // SearchView
        SearchManager manager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);

        final SearchView search = (SearchView) menu.findItem(R.id.search).getActionView();

        search.setSuggestionsAdapter(new ExampleAdapter(this, cursor, items));

    }

}

Now you need to create an adapter extended from CursorAdapter:

ExampleAdapter.java

public class ExampleAdapter extends CursorAdapter {

    private List<String> items;

    private TextView text;

    public ExampleAdapter(Context context, Cursor cursor, List<String> items) {

        super(context, cursor, false);

        this.items = items;

    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        text.setText(items.get(cursor.getPosition()));

    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {

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

        View view = inflater.inflate(R.layout.item, parent, false);

        text = (TextView) view.findViewById(R.id.text);

        return view;

    }

}

A better way to do this is if your list data is from a database, you can pass the Cursor returned by database functions directly to ExampleAdapter and use the relevant column selector to display the column text in the TextView referenced in the adapter.

Please note: when you import CursorAdapter don't import the Android support version, import the standard android.widget.CursorAdapter instead.

The adapter will also require a custom layout:

res/layout/item.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <TextView
        android:id="@+id/item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

You can now customize list items by adding additional text or image views to the layout and populating them with data in the adapter.

This should be all, but if you haven't done this already you need a SearchView menu item:

res/menu/example.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/search"
        android:title="@string/search"
        android:showAsAction="ifRoom"
        android:actionViewClass="android.widget.SearchView" />

</menu>

Then create a searchable configuration:

res/xml/searchable.xml

<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/search"
    android:hint="@string/search" >
</searchable>

Finally add this inside the relevant activity tag in the manifest file:

AndroidManifest.xml

<intent-filter>
    <action android:name="android.intent.action.SEARCH" />
</intent-filter>

<meta-data
    android:name="android.app.default_searchable"
    android:value="com.example.ExampleActivity" />
<meta-data
    android:name="android.app.searchable"
    android:resource="@xml/searchable" />

Please note: The @string/search string used in the examples should be defined in values/strings.xml, also don't forget to update the reference to com.example for your project.

Kaiser
  • 606
  • 8
  • 22
tpbapp
  • 2,506
  • 2
  • 19
  • 21
  • 3
    Hi, but one doubt, i hope you will help me on this. If i type one letter on filter, though i have filter results, nothing will display, if its more then one. then only it displays. why? – Naruto May 30 '14 at 19:44
  • Hi, maybe check your styles for anything related to EditText or AutoCompleteTextView as you can set the default number of characters that need to be entered before it will show autocomplete. Actually I think the default is 2 characters, so it might be the case you would need to add this to styles to change it. – tpbapp May 31 '14 at 02:34
  • Hi, while developing i came accross one more issue of searchview. if i display more item in drop down, eg: 3 textviews, then size of item increases, so the search items flows on keyboard if i scroll like http://i.stack.imgur.com/d7t3A.png. This happens if i increase size of text or add more views in item. Is it default behaviour? – Naruto May 31 '14 at 10:19
  • Hi again, no that should not be happening. It sounds like you have a lot of code already in the project that is interfering with the AutoCompleteTextView in the SearchView or perhaps there's something wrong with the keyboard software, and app or a plugin causing this behaviour. The default behavior should be something like this http://stackoverflow.com/questions/22116237/autocomplete-textview-suggestion-list-is-behind-keyboard except this person is actually asking how to get it to *not* be hidden behind the keyboard. – tpbapp May 31 '14 at 15:38
  • Thanks for the code, Can You describe more about these 3 lines of ur code ` String[] columns = new String[] { "_id", "text" }; Object[] temp = new Object[] { 0, "default" }; MatrixCursor cursor = new MatrixCursor(columns);` _And also the search query is not running properly, each letter I type it displays the complete list of items in a black background with black item which is very hard to read, & it has to do nothing with the text we type, it always shows all the items in the list_. Please reply... – Vivek Warde Jun 30 '14 at 18:33
  • @vwvwvwvwvwvwvwvwvw sounds like you have some styles defined in XML somewhere in your application that are being inherited by the generated drop down list. Those lines of code are to convert an ArrayList into a Cursor, but you could also just use a Cursor as would be returned directly from a database query. Hope this helps. – tpbapp Jun 30 '14 at 22:19
  • What would be the best way to assign a click listener to the suggestion then as well? Would that be in the BindView() method? Thanks – John Shelley Sep 15 '14 at 19:15
  • @JohnShelley I think it would go in the activity onCreateOptionMenu after the SearchView value has been set, for example where the query listener is. See this for more info http://stackoverflow.com/questions/14602807/android-how-to-detect-click-on-custom-searchview-element – tpbapp Sep 17 '14 at 08:43
  • @JohnShelley apply a setOnSuggestionListener to the SearchView object to listen for when the user clicks on a suggestion. In the example above, you would do this in the loadHistory() method. – JDJ Sep 19 '14 at 01:00
  • @TPBApp, Hi, nice solution TPBApp, I also using this solution. But there's problem, when I type the second word query, the suggestion list doesn't appear. For example, when I type "P", there is a suggestion list appear. But when I type next word, for example "a" (so the query will be "Pa"), the suggestion list doesnt appear. The Logcat show warning "IInputConnectionWrapper(14371): endBatchEdit on inactive InputConnection". I've checked in the adapter, seems like the bindView() and newView() method isnt being called, so the suggestion list doesnt appear. But I dont know why... Can u help? – Hafizh Herdi Oct 29 '14 at 09:18
  • @HafizhHerdi You will probably need to post this as a separate question, but you might find some help somewhere in the comments of the tutorial I wrote for this http://tpbapp.com/android-development/android-action-bar-searchview-tutorial#comments – tpbapp Oct 30 '14 at 13:36
  • 3
    @tpbapp I already figure it out. The reason suggestion list doesn't appearing is because in your code you had `search.setSearchableInfo(manager.getSearchableInfo(getComponentName()));` called twice in the `onCreateOptionsMenu()` and `loadHistory()` methods. So I make the SearchView and SearchManager global variable, and only called `getSearchableInfo()` once in `onCreateOptionsMenu()`. And my suggestion list is now working perfectly. I suggest you to revised your code. – Hafizh Herdi Oct 31 '14 at 08:51
  • @tpbapp How do you do this on fragments? I can't get my search view on a fragment to work. – Sndn Feb 23 '15 at 06:32
  • Thanks, with some corrects i am able to implement !! – Ramesh_D Apr 04 '15 at 12:08
  • @tpbapp: Thanks for the solution. can you please suggest me what is the `R.id.text` in the `text = (TextView) view.findViewById(R.id.text);` on `newView()` function in ExampleAdapter.java class. I think I need to refer to the SearchWidget's text field. Let me know if I am right or some pointers otherwise. – Harsh Vardhan Jun 11 '15 at 15:32
  • I've been at this all night!!! You had the correct solution that let me not use support libraries, letting me keep a material theme! THANK YOU! – StarWind0 Nov 07 '15 at 09:25
  • To solve the problem of showing the history suggestion that appear from the 2nd character, go over this answer https://stackoverflow.com/questions/24260101/searchview-suggestion-doesnt-work-with-one-character#answer-24279952 – Zain Jul 17 '19 at 00:27
56

If anyone else is having a nullptr on the searchview variable, I found out that the item setup is a tiny bit different:

old:

android:showAsAction="ifRoom"
android:actionViewClass="android.widget.SearchView"

new:

app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="androidx.appcompat.widget.SearchView"

pre-android x:

app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView"

For more information, it's updated documentation is located here.

Grease
  • 1,308
  • 11
  • 12
5

SearchDialog or SearchWidget ?

When it comes to implement a search functionality there are two suggested approach by official Android Developer Documentation.
You can either use a SearchDialog or a SearchWidget.
I am going to explain the implementation of Search functionality using SearchWidget.

How to do it with Search widget ?

I will explain search functionality in a RecyclerView using SearchWidget. It's pretty straightforward.

Just follow these 5 Simple steps

1) Add searchView item in the menu

You can add SearchView can be added as actionView in menu using

app:useActionClass = "android.support.v7.widget.SearchView" .

<menu 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"
   tools:context="rohksin.com.searchviewdemo.MainActivity">
   <item
       android:id="@+id/searchBar"
       app:showAsAction="always"
       app:actionViewClass="android.support.v7.widget.SearchView"
   />
</menu>

2) Set up SerchView Hint text, listener etc

You should initialize SearchView in the onCreateOptionsMenu(Menu menu) method.

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
     // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);

        MenuItem searchItem = menu.findItem(R.id.searchBar);

        SearchView searchView = (SearchView) searchItem.getActionView();
        searchView.setQueryHint("Search People");
        searchView.setOnQueryTextListener(this);
        searchView.setIconified(false);

        return true;
   }

3) Implement SearchView.OnQueryTextListener in your Activity

OnQueryTextListener has two abstract methods

  1. onQueryTextSubmit(String query)
  2. onQueryTextChange(String newText

So your Activity skeleton would look like this

YourActivity extends AppCompatActivity implements SearchView.OnQueryTextListener{

     public boolean onQueryTextSubmit(String query)

     public boolean onQueryTextChange(String newText) 

}

4) Implement SearchView.OnQueryTextListener

You can provide the implementation for the abstract methods like this

public boolean onQueryTextSubmit(String query) {

    // This method can be used when a query is submitted eg. creating search history using SQLite DB

    Toast.makeText(this, "Query Inserted", Toast.LENGTH_SHORT).show();
    return true;
}

@Override
public boolean onQueryTextChange(String newText) {

    adapter.filter(newText);
    return true;
}

5) Write a filter method in your RecyclerView Adapter.

Most important part. You can write your own logic to perform search.
Here is mine. This snippet shows the list of Name which contains the text typed in the SearchView

public void filter(String queryText)
{
    list.clear();

    if(queryText.isEmpty())
    {
       list.addAll(copyList);
    }
    else
    {

       for(String name: copyList)
       {
           if(name.toLowerCase().contains(queryText.toLowerCase()))
           {
              list.add(name);
           }
       }

    }

   notifyDataSetChanged();
}

Relevant link:

Full working code on SearchView with an SQLite database in this Music App

Rohit Singh
  • 16,950
  • 7
  • 90
  • 88
1

For Searchview use these code

  1. For XML

    <android.support.v7.widget.SearchView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/searchView">
    
    </android.support.v7.widget.SearchView>
    

  2. In your Fragment or Activity

    package com.example.user.salaryin;
    
    import android.app.ProgressDialog;
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v4.view.MenuItemCompat;
    import android.support.v7.widget.GridLayoutManager;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.support.v7.widget.SearchView;
    import android.view.LayoutInflater;
    import android.view.Menu;
    import android.view.MenuInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.Toast;
    import com.example.user.salaryin.Adapter.BusinessModuleAdapter;
    import com.example.user.salaryin.Network.ApiClient;
    import com.example.user.salaryin.POJO.ProductDetailPojo;
    import com.example.user.salaryin.Service.ServiceAPI;
    import java.util.ArrayList;
    import java.util.List;
    import retrofit2.Call;
    import retrofit2.Callback;
    import retrofit2.Response;
    
    
    public class OneFragment extends Fragment implements SearchView.OnQueryTextListener {
    
    RecyclerView recyclerView;
    RecyclerView.LayoutManager layoutManager;
    ArrayList<ProductDetailPojo> arrayList;
    BusinessModuleAdapter adapter;
    private ProgressDialog pDialog;
    GridLayoutManager gridLayoutManager;
    SearchView searchView;
    
    public OneFragment() {
        // Required empty public constructor
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
    
        View rootView = inflater.inflate(R.layout.one_fragment,container,false);
    
        pDialog = new ProgressDialog(getActivity());
        pDialog.setMessage("Please wait...");
    
    
        searchView=(SearchView)rootView.findViewById(R.id.searchView);
        searchView.setQueryHint("Search BY Brand");
        searchView.setOnQueryTextListener(this);
    
        recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
        layoutManager = new LinearLayoutManager(this.getActivity());
        recyclerView.setLayoutManager(layoutManager);
        gridLayoutManager = new GridLayoutManager(this.getActivity().getApplicationContext(), 2);
        recyclerView.setLayoutManager(gridLayoutManager);
        recyclerView.setHasFixedSize(true);
        getImageData();
    
    
        // Inflate the layout for this fragment
        //return inflater.inflate(R.layout.one_fragment, container, false);
        return rootView;
    }
    
    
    private void getImageData() {
        pDialog.show();
        ServiceAPI service = ApiClient.getRetrofit().create(ServiceAPI.class);
        Call<List<ProductDetailPojo>> call = service.getBusinessImage();
    
        call.enqueue(new Callback<List<ProductDetailPojo>>() {
            @Override
            public void onResponse(Call<List<ProductDetailPojo>> call, Response<List<ProductDetailPojo>> response) {
                if (response.isSuccessful()) {
                    arrayList = (ArrayList<ProductDetailPojo>) response.body();
                    adapter = new BusinessModuleAdapter(arrayList, getActivity());
                    recyclerView.setAdapter(adapter);
                    pDialog.dismiss();
                } else if (response.code() == 401) {
                    pDialog.dismiss();
                    Toast.makeText(getActivity(), "Data is not found", Toast.LENGTH_SHORT).show();
                }
    
            }
    
            @Override
            public void onFailure(Call<List<ProductDetailPojo>> call, Throwable t) {
                Toast.makeText(getActivity(), t.getMessage(), Toast.LENGTH_SHORT).show();
                pDialog.dismiss();
    
            }
        });
    }
    
       /* @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        getActivity().getMenuInflater().inflate(R.menu.menu_search, menu);
        MenuItem menuItem = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(menuItem);
        searchView.setQueryHint("Search Product");
        searchView.setOnQueryTextListener(this);
    }*/
    
    @Override
    public boolean onQueryTextSubmit(String query) {
        return false;
    }
    
    @Override
    public boolean onQueryTextChange(String newText) {
        newText = newText.toLowerCase();
        ArrayList<ProductDetailPojo> newList = new ArrayList<>();
        for (ProductDetailPojo productDetailPojo : arrayList) {
            String name = productDetailPojo.getDetails().toLowerCase();
    
            if (name.contains(newText) )
                newList.add(productDetailPojo);
            }
        adapter.setFilter(newList);
        return true;
       }
    }
    
  3. In adapter class

     public void setFilter(List<ProductDetailPojo> newList){
        arrayList=new ArrayList<>();
        arrayList.addAll(newList);
        notifyDataSetChanged();
    }
    
amit
  • 709
  • 6
  • 17