Turn AutoCompleteTextView into a SearchView in ActionBar instead

40,427

Solution 1

To get Places Autocomplete API results in a SearchView, you'll first need a ContentProvider for the API.

import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.Log;

public class PlacesSuggestionProvider extends ContentProvider {
    private static final String LOG_TAG = "ExampleApp";

    public static final String AUTHORITY = "com.example.google.places.search_suggestion_provider";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/search");

    // UriMatcher constant for search suggestions
    private static final int SEARCH_SUGGEST = 1;

    private static final UriMatcher uriMatcher;

    private static final String[] SEARCH_SUGGEST_COLUMNS = {
            BaseColumns._ID,
            SearchManager.SUGGEST_COLUMN_TEXT_1,
            SearchManager.SUGGEST_COLUMN_TEXT_2,
            SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
    };

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
        uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
    }

    @Override
    public int delete(Uri uri, String arg1, String[] arg2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)) {
            case SEARCH_SUGGEST:
                return SearchManager.SUGGEST_MIME_TYPE;
            default:
                throw new IllegalArgumentException("Unknown URL " + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues arg1) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
            String sortOrder) {
        Log.d(LOG_TAG, "query = " + uri);

        // Use the UriMatcher to see what kind of query we have
        switch (uriMatcher.match(uri)) {
            case SEARCH_SUGGEST:
                Log.d(LOG_TAG, "Search suggestions requested.");
                MatrixCursor cursor = new MatrixCursor(SEARCH_SUGGEST_COLUMNS, 1);
                cursor.addRow(new String[] {
                        "1", "Search Result", "Search Result Description", "content_id"
                });
                return cursor;
            default:
                throw new IllegalArgumentException("Unknown Uri: " + uri);
        }
    }

    @Override
    public int update(Uri uri, ContentValues arg1, String arg2, String[] arg3) {
        throw new UnsupportedOperationException();
    }
}

Then add your Places Autocomplete API client code into the query method on the content provider. You extract the user input as follows:

String query = uri.getLastPathSegment().toLowerCase();

Add the PlacesSuggestionProvider to your AndroidManifest, and make sure your activity has a searchable configuration.

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >

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

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />
        </activity>

        <provider
            android:name="com.example.google.places.PlacesSuggestionProvider"
            android:authorities="com.example.google.places.search_suggestion_provider"
            android:syncable="false" />
    </application>

</manifest>

And make sure your searchable configuration (res/xml/searchable.xml) has a search suggest authority.

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_name"
    android:hint="@string/search_hint"
    android:searchSuggestAuthority="com.example.google.places.search_suggestion_provider">
</searchable>

The authority should be the same in AndroidManifest.xml, searchable.xml, and your content provider.

Create a options menu for your ActionBar that includes a SearchView (/res/menu/options_menu.xml).

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_search"
          android:title="@string/menu_search"
          android:icon="@drawable/ic_menu_search"
          android:showAsAction="collapseActionView|ifRoom"
          android:actionViewClass="android.widget.SearchView" />
</menu>

Configure your Activity with a SearchView that's associated with your searchable configuration/

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the options menu from XML
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.options_menu, menu);

    SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
    // Tells your app's SearchView to use this activity's searchable configuration
    searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
    searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default

    return true;
}

A few key docs are:

Adding Custom Suggestions: http://developer.android.com/guide/topics/search/adding-custom-suggestions.html

Creating a Content Provider: http://developer.android.com/guide/topics/providers/content-provider-creating.html

Using a Search Widget: http://developer.android.com/guide/topics/search/search-dialog.html#UsingSearchWidget

Solution 2

AutoCompleteTextView with google search Api

enter image description here

your xml

  <AutoCompleteTextView
    android:id="@+id/main_omnibox_input"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:background="@null" 
    android:hint="Search"  
    android:focusable="true"
    android:focusableInTouchMode="true"                                  
    android:selectAllOnFocus="true"
    android:singleLine="true" 
    android:textSize="@dimen/_14sdp" />

your java code

  this.inputBox = (AutoCompleteTextView) findViewById(R.id.main_omnibox_input);
  inputBox.setAdapter(new SearchAutocompleteAdapter(SearchActivity.this, new SearchAutocompleteAdapter.OnSearchCommitListener() {
        @Override
        public void onSearchCommit(String text) {
            inputBox.setText(text);
            inputBox.setSelection(text.length());
        }
    }));


    this.inputBox.setOnItemClickListener(new OnItemClickListener() {
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long j) {
            String charSequence = ((TextView) view.findViewById(android.R.id.text1)).getText().toString();
            inputBox.setText(Html.fromHtml(BrowserUnit.urlWrapper(charSequence)), BufferType.SPANNABLE);
            inputBox.setSelection(charSequence.length());
           // your code
           // updateAlbum(charSequence);
           // hideSoftInput(SearchActivity.this.inputBox);
        }
    });

SearchAutocompleteAdapter

public class SearchAutocompleteAdapter extends BaseAdapter implements Filterable {

interface OnSearchCommitListener {
    void onSearchCommit(String text);
}

private final Context mContext;
private final OnSearchCommitListener commitListener;
private List<String> completions = new ArrayList<>();
static final String searchCompleteUrl = "https://www.google.com/complete/search?client=firefox&q=%s";

SearchAutocompleteAdapter(Context context, OnSearchCommitListener commitListener) {
    mContext = context;
    this.commitListener = commitListener;
}

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

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

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

@SuppressLint("ClickableViewAccessibility")
@Override
@SuppressWarnings("ConstantConditions")
public View getView(final int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
    }
    TextView textview = convertView.findViewById(android.R.id.text1);
    textview.setText(completions.get(position));
    Drawable d = ContextCompat.getDrawable(mContext, R.drawable.icon_goarrowsmall);
    final int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32, mContext.getResources().getDisplayMetrics());
    d.setBounds(0, 0, size, size);
    textview.setCompoundDrawables(null, null, d, null);

    textview.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            if (event.getAction() != MotionEvent.ACTION_DOWN) {
                return false;
            }
            TextView t = (TextView) view;
            if (event.getX() > t.getWidth() - t.getCompoundPaddingRight()) {
                commitListener.onSearchCommit(getItem(position).toString());
                return true;
            }
            return false;
        }
    });
    parent.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            if (event.getX() > view.getWidth() - size * 2) {
                return true;
            }
            return false;
        }
    });
    return convertView;
}

@Override
public Filter getFilter() {
    return new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            // Invoked on a worker thread
            FilterResults filterResults = new FilterResults();
            if (constraint != null) {
                List<String> results = getCompletions(constraint.toString());
                filterResults.values = results;
                filterResults.count = results.size();
            }
            return filterResults;
        }

        @Override
        @SuppressWarnings("unchecked")
        protected void publishResults(CharSequence constraint, FilterResults results) {
            if (results != null && results.count > 0) {
                completions = (List<String>) results.values;
                notifyDataSetChanged();
            } else {
                notifyDataSetInvalidated();
            }
        }
    };
}

private List<String> getCompletions(String text) {
    int total = 0;
    byte[] data = new byte[16384];
    try {
        URL url = new URL(URLUtil.composeSearchUrl(text, searchCompleteUrl, "%s"));
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        try {
            InputStream in = new BufferedInputStream(urlConnection.getInputStream());
            while (total <= data.length) {
                int count = in.read(data, total, data.length - total);
                if (count == -1) {
                    break;
                }
                total += count;
            }
            if (total == data.length) {
                // overflow
                return new ArrayList<>();
            }
        } finally {
            urlConnection.disconnect();
        }
    } catch (IOException e) {
        return new ArrayList<>();
    }

    JSONArray jsonArray;
    try {
        jsonArray = new JSONArray(new String(data, StandardCharsets.UTF_8));
    } catch (JSONException e) {
        return new ArrayList<>();
    }
    jsonArray = jsonArray.optJSONArray(1);
    if (jsonArray == null) {
        return new ArrayList<>();
    }
    final int MAX_RESULTS = 10;
    List<String> result = new ArrayList<>(Math.min(jsonArray.length(), MAX_RESULTS));
    for (int i = 0; i < jsonArray.length() && result.size() < MAX_RESULTS; i++) {
        String s = jsonArray.optString(i);
        if (s != null && !s.isEmpty()) {
            result.add(s);
        }
    }
    return result;
}

}

Share:
40,427

Related videos on Youtube

theAlse
Author by

theAlse

Embedded Software Engineer SOreadytohelp

Updated on July 11, 2020

Comments

  • theAlse
    theAlse almost 4 years

    I have a AutoCompleteTextView which gives user auto-completion search result from Google Places API. Once I was done I discovered SearchView and how it can be placed in the ActionBar. I checked out the SearchView example provided by google and added it to my application as a starting point (it lists the installed applications) but I don´t know how to proceed from here. I want to have the same functionality as AutoCompleteTextView but use the SearchView instead. Any suggestion? The whole class is provided below.

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.URL;
    import java.net.URLConnection;
    import java.net.URLEncoder;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import android.app.Activity;
    import android.app.SearchManager;
    import android.app.SearchableInfo;
    import android.content.Context;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.os.Handler;
    import android.preference.PreferenceManager;
    import android.text.Editable;
    import android.text.TextWatcher;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.Menu;
    import android.view.MenuInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.Window;
    import android.view.animation.Animation;
    import android.view.animation.AnimationUtils;
    import android.widget.AdapterView;
    import android.widget.AdapterView.OnItemClickListener;
    import android.widget.ArrayAdapter;
    import android.widget.AutoCompleteTextView;
    import android.widget.ImageView;
    import android.widget.ListView;
    import android.widget.SearchView;
    import android.widget.TextView;
    import android.widget.Toast;
    
    
    
    /**
     * 
     * @author 
     *
     */
    public class PlacesListSearchActivity extends Activity implements SearchView.OnQueryTextListener{
    
        private static final String TAG = "PlacesListActivity";
    
        private ResultReceiver mReceiver;
    
        private OnSharedPreferenceChangeListener sharedPreferencesListener;
        private SharedPreferences sharedPreferences;
    
        /** Called when the activity is first created. */
        public ArrayAdapter<String> adapter;
        public AutoCompleteTextView textView;
    
        private SearchView mSearchView;
        private TextView mStatusView;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
            setContentView(R.layout.places_search);
            mStatusView = (TextView) findViewById(R.id.status_text);
    
    
            final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.item_list);
            textView = (AutoCompleteTextView)findViewById(R.id.autoCompleteTextView1);
            adapter.setNotifyOnChange(true);
            textView.setHint("type store name");
            textView.setAdapter(adapter);
            textView.addTextChangedListener(new TextWatcher() {
    
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                if (count%3 == 1) {
                    adapter.clear();
                        GetPlaces task = new GetPlaces();
                        //now pass the argument in the textview to the task
                        task.execute(textView.getText().toString());
                }
            }
    
            public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
            // TODO Auto-generated method stub
    
            }
    
            public void afterTextChanged(Editable s) {
    
            }
    
            });
        }
    
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            super.onCreateOptionsMenu(menu);
    
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.searchview_in_menu, menu);
            MenuItem searchItem = menu.findItem(R.id.action_search);
            mSearchView = (SearchView) searchItem.getActionView();
            setupSearchView(searchItem);
    
            return true;
        }
    
        private void setupSearchView(MenuItem searchItem) {
    
            if (isAlwaysExpanded()) {
                mSearchView.setIconifiedByDefault(false);
            } else {
                searchItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM
                        | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
            }
    
            SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
            if (searchManager != null) {
                List<SearchableInfo> searchables = searchManager.getSearchablesInGlobalSearch();
    
                // Try to use the "applications" global search provider
                SearchableInfo info = searchManager.getSearchableInfo(getComponentName());
                for (SearchableInfo inf : searchables) {
                    if (inf.getSuggestAuthority() != null
                            && inf.getSuggestAuthority().startsWith("applications")) {
                        info = inf;
                    }
                }
                mSearchView.setSearchableInfo(info);
            }
    
            mSearchView.setOnQueryTextListener(this);
        }
    
        public boolean onQueryTextChange(String newText) {
            mStatusView.setText("Query = " + newText);
            return false;
        }
    
        public boolean onQueryTextSubmit(String query) {
            mStatusView.setText("Query = " + query + " : submitted");
            return false;
        }
    
        public boolean onClose() {
            mStatusView.setText("Closed!");
            return false;
        }
    
        protected boolean isAlwaysExpanded() {
            return false;
        }
    
        class GetPlaces extends AsyncTask<String, Void, ArrayList<String>> {
            @Override
            // three dots is java for an array of strings
            protected ArrayList<String> doInBackground(String... args)
            {
                Log.d("PlacesListActivity", "doInBackground");
                ArrayList<String> predictionsArr = new ArrayList<String>();
                try
                {
                    URL googlePlaces = new URL(
                            "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=" +
                            URLEncoder.encode(args[0], "UTF-8") +
    //                      "&types=geocode&language=en&sensor=true&key=" + SEARCHES FOR GEO CODES
                            "&types=establishment&language=en&sensor=true&key=" +
                            getResources().getString(R.string.googleAPIKey));
    
                    Log.d("PlacesListActivity", googlePlaces.toString());
    
                    URLConnection tc = googlePlaces.openConnection();
                    BufferedReader in = new BufferedReader(new InputStreamReader(
                            tc.getInputStream()));
    
                    String line;
                    StringBuffer sb = new StringBuffer();
                    //take Google's legible JSON and turn it into one big string.
                    while ((line = in.readLine()) != null) {
                        sb.append(line);
                    }
                    //turn that string into a JSON object
                    JSONObject predictions = new JSONObject(sb.toString()); 
                    //now get the JSON array that's inside that object            
                    JSONArray ja = new JSONArray(predictions.getString("predictions"));
    
                    for (int i = 0; i < ja.length(); i++) {
                        JSONObject jo = (JSONObject) ja.get(i);
                        //add each entry to our array
                        predictionsArr.add(jo.getString("description"));
                    }
                } catch (IOException e)
                {
                Log.e("PlacesListActivity", "GetPlaces : doInBackground", e);
                } catch (JSONException e)
                {
                Log.e("PlacesListActivity", "GetPlaces : doInBackground", e);
                }
    
                return predictionsArr;
            }
    
            @Override
            protected void onPostExecute(ArrayList<String> result){
                Log.d("PlacesListActivity", "onPostExecute : " + result.size());
                //update the adapter
                adapter = new ArrayAdapter<String>(getBaseContext(), R.layout.item_list);
                adapter.setNotifyOnChange(true);
                //attach the adapter to textview
                textView.setAdapter(adapter);
    
                for (String string : result) {
                    Log.d("PlacesListActivity", "onPostExecute : result = " + string);
                    adapter.add(string);
                    adapter.notifyDataSetChanged();
                }
    
                Log.d("PlacesListActivity", "onPostExecute : autoCompleteAdapter" + adapter.getCount());
            }
    
        }
    
    }
    

    After updating the code suggested by saxman, I can see that the query method in the provider is never called:

    My manifest file looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.rathinavelu.rea"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk
            android:minSdkVersion="7"
            android:targetSdkVersion="15" />
    
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
        <application
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name" >
    
            <activity
                android:name=".MainActivity"
                android:label="@string/app_name"
                android:screenOrientation="portrait" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <activity android:name=".PlacesListActivity" >
            </activity>
    
            <activity android:name=".PlacesListSearchActivity" >
                <action android:name="android.intent.action.SEARCH" />
                <meta-data
                    android:name="android.app.searchable"
                    android:resource="@xml/searchable" />
            </activity>
    
            <activity android:name=".TestMapActivity" >
            </activity>
    
            <activity android:name=".SettingsPreferencesActivity" >
            </activity>
    
            <activity android:name="com.rathinavelu.util.ConnectionChecker" >
            </activity>
    
            <uses-library android:name="com.google.android.maps" />
    
            <service
                android:name=".places.PlacesRESTService"
                android:enabled="true"
                android:exported="false" >
                <intent-filter>
                    <action android:name="android.intent.action.ACTION_SYNC" />
                </intent-filter>
            </service>
    
            <provider
                android:name=".places.PlacesSuggestionProvider"
                android:authorities="com.rathinavelu.rea.places.search_suggestion_provider"
                android:syncable="false" />
    
        </application>
    
    </manifest>
    

    I use the same authority in the manifest, content provider and manifest file. I see the searchView in the menu and I have not modified the query method so It should just return the one line cursor. but the query method is never called. please help. Another issue I just spotted is that the searchView does not show the specified search_hint!

    Providing more code *PlacesListSearchActivity.java*

    public class PlacesListSearchActivity extends Activity {
    
            private static final String TAG = "PlacesListSearchActivity";
    
            private ResultReceiver mReceiver;
    
            private OnSharedPreferenceChangeListener sharedPreferencesListener;
            private SharedPreferences sharedPreferences;
    
        /** Called when the activity is first created. */
            public ArrayAdapter<String> adapter;
            public AutoCompleteTextView textView;
    
            private SearchView mSearchView;
            private TextView mStatusView;
    
            @Override
        public void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
                    setContentView(R.layout.places_search);
                    mStatusView = (TextView) findViewById(R.id.status_text);
    
    
                    final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.item_list);
                    textView = (AutoCompleteTextView)findViewById(R.id.autoCompleteTextView1);
                    adapter.setNotifyOnChange(true);
                    textView.setHint("type store name");
                    textView.setAdapter(adapter);
                    textView.addTextChangedListener(new TextWatcher() {
    
                    public void onTextChanged(CharSequence s, int start, int before, int count) {
                            if (count%3 == 1) {
                                    adapter.clear();
                                            GetPlaces task = new GetPlaces();
                                            //now pass the argument in the textview to the task
                                            task.execute(textView.getText().toString());
                            }
                    }
    
                    public void beforeTextChanged(CharSequence s, int start, int count,
                    int after) {
                    // TODO Auto-generated method stub
    
                    }
    
                    public void afterTextChanged(Editable s) {
    
                    }
    
                    });
            }
    
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
    //        super.onCreateOptionsMenu(menu);
            // Inflate the options menu from XML
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.places_list_search_options_menu, menu);
    
            SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
            SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
            // Tells your app's SearchView to use this activity's searchable configuration
            searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
            searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
    
    //        setupSearchView(searchItem);
    
            return true;
        }
    

    places_search.xml

    <?xml version="1.0" encoding="utf-8"?>
    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#dddddd">
    
    <AutoCompleteTextView android:id="@+id/autoCompleteTextView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp" >
            <requestFocus></requestFocus>
            </AutoCompleteTextView>
    
        <TextView
                android:id="@+id/status_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center_horizontal"/>
    </RelativeLayout>
    

    places_list_search_options_menu.xml

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android" >
        <item android:id="@+id/menu_search"
              android:title="@string/menu_search"
              android:icon="@android:drawable/ic_menu_search"
              android:showAsAction="collapseActionView|ifRoom"
              android:actionViewClass="android.widget.SearchView" />
    </menu>
    

    searchable.xml

    <?xml version="1.0" encoding="utf-8"?>
    <searchable xmlns:android="http://schemas.android.com/apk/res/android"
        android:label="@string/app_name"
        android:hint="@string/search_hint"
        android:searchSuggestAuthority="com.rathinavelu.rea.places.search_suggestion_provider">
    </searchable>
    

    PlacesSuggestionProvider.java

    public class PlacesSuggestionProvider extends ContentProvider {
        private static final String LOG_TAG = "PlacesSuggestionProvider";
    
        public static final String AUTHORITY = "com.rathinavelu.rea.places.search_suggestion_provider";
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/search");
    
        // UriMatcher constant for search suggestions
        private static final int SEARCH_SUGGEST = 1;
    
        private static final UriMatcher uriMatcher;
    
        private static final String[] SEARCH_SUGGEST_COLUMNS = {
                BaseColumns._ID,
                SearchManager.SUGGEST_COLUMN_TEXT_1,
                SearchManager.SUGGEST_COLUMN_TEXT_2,
                SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
        };
    
        static {
            uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
            uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
            uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
        }
    
        @Override
        public int delete(Uri uri, String arg1, String[] arg2) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public String getType(Uri uri) {
            switch (uriMatcher.match(uri)) {
                case SEARCH_SUGGEST:
                    return SearchManager.SUGGEST_MIME_TYPE;
                default:
                    throw new IllegalArgumentException("Unknown URL " + uri);
            }
        }
    
        @Override
        public Uri insert(Uri uri, ContentValues arg1) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public boolean onCreate() {
            return true;
        }
    
        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                String sortOrder) {
            Log.d(LOG_TAG, "query = " + uri);
    
            // Use the UriMatcher to see what kind of query we have
            switch (uriMatcher.match(uri)) {
                case SEARCH_SUGGEST:
                    Log.d(LOG_TAG, "Search suggestions requested.");
                    MatrixCursor cursor = new MatrixCursor(SEARCH_SUGGEST_COLUMNS, 1);
                    cursor.addRow(new String[] {
                            "1", "Search Result", "Search Result Description", "content_id"
                    });
                    return cursor;
                default:
                    throw new IllegalArgumentException("Unknown Uri: " + uri);
            }
        }
    
        @Override
        public int update(Uri uri, ContentValues arg1, String arg2, String[] arg3) {
            throw new UnsupportedOperationException();
        }
    }
    
    • Lumii
      Lumii about 11 years
      Hi, just wondering, is there any advantage for using SearchView over AutoCompleteTextView for search functionality in action bar?
    • Fatima
      Fatima over 8 years
      Terrific link for future vsitors; drzon.net/…
  • theAlse
    theAlse almost 12 years
    thanks for the reply. So the content provider will do the looking up at Google Places and return a cursor! But I still don´t know how the searchView is connected to it?
  • saxman
    saxman almost 12 years
    Added that code as well (last 2 code blocks). In onCreateOptionsMenu, you configure the SearchView to use the searchable configuration you defined for your activity.
  • saxman
    saxman almost 12 years
    It sounds like you might be missing the last part: associating your searchable config with the SearchView. Please post your other files as well (searchable.xml, PlacesListSearchActivity, PlaceSuggestionProvider, and whatever you're using for the options menu).
  • theAlse
    theAlse almost 12 years
    I have added pretty much all the classes. searchable.xml & PlacesSuggestionProvider.java is pretty much your original code.
  • saxman
    saxman almost 12 years
    You need an <intent-filter> around the <action android:name="android.intent.action.SEARCH" /> in your manifest.
  • theAlse
    theAlse almost 12 years
    I will try this as soon as I get home ;) I can not believe I missed that and also I don´t understand why it did not complain!
  • theAlse
    theAlse almost 12 years
    A follow up question, how do I use the previously used onPostExecute into the query-method of the content provider?
  • saxman
    saxman almost 12 years
    Not sure I follow... But you don't need to use an AsyncTask in your ContentProvider query method; calls to query(...) are off the UI thread. For my implementation, I have a PlacesService class that has "autocomplete", "getDetails", and "search" methods. I call autocomplete from the search suggest ContentProvider, but can also call it from other widgets, such as the AutoCompleteTextView.
  • Elyes Jlassi
    Elyes Jlassi over 9 years
    Is it possible to add two search widget in the same action bar of an activity ?
  • Aman Jham
    Aman Jham almost 9 years
    How to find searchView onItemClickListner?
  • kellogs
    kellogs over 8 years
    Thanks for the solution! A few comments: query method does run on the UI thread on API 19 at least. I am getting android.os.NetworkOnMainThreadException if ran just like that. Another issue I have spent 2 hours on: Replace the line public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/search"); with public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + SearchManager.SUGGEST_URI_PATH_QUERY); and all should be well
  • kellogs
    kellogs over 8 years
    If going the way described above then the search query will not be available in String query = uri.getLastPathSegment().toLowerCase(); but instea you may take it irectly from the query method arguments