web-dev-qa-db-fra.com

Transformez AutoCompleteTextView en SearchView dans ActionBar à la place

J'ai un AutoCompleteTextView qui donne le résultat de la recherche de saisie semi-automatique de l'utilisateur de Google Places API. Une fois que j'ai fini, j'ai découvert SearchView et comment le placer dans ActionBar. J'ai vérifié l'exemple SearchView fourni par Google et l'ai ajouté à mon application comme point de départ (il répertorie les applications installées) mais je ne sais pas comment procéder à partir d'ici. Je veux avoir les mêmes fonctionnalités que AutoCompleteTextView mais utiliser plutôt SearchView. Toute suggestion? La classe entière est fournie ci-dessous.

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());
        }

    }

}

Après avoir mis à jour le code suggéré par saxman, je peux voir que la méthode de requête dans le fournisseur n'est jamais appelée:

Mon fichier manifeste ressemble à ceci:

<?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>

J'utilise la même autorité dans le manifeste, le fournisseur de contenu et le fichier manifeste. Je vois le searchView dans le menu et je n'ai pas modifié la méthode de requête donc il devrait simplement retourner le curseur d'une ligne. mais la méthode de requête n'est jamais appelée. veuillez aider. Un autre problème que je viens de remarquer est que le searchView n'affiche pas le search_hint spécifié!

Fournir plus de 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>

consultable.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();
    }
}
27
theAlse

Pour obtenir les résultats de l'API de saisie semi-automatique Google Adresses dans un SearchView, vous aurez d'abord besoin d'un ContentProvider pour l'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();
    }
}

Ajoutez ensuite votre code client API de saisie semi-automatique Places dans la méthode de requête du fournisseur de contenu. Vous extrayez l'entrée utilisateur comme suit:

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

Ajoutez le PlacesSuggestionProvider à votre AndroidManifest et assurez-vous que votre activité a une configuration consultable.

    <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>

Et assurez-vous que votre configuration consultable (res/xml/searchable.xml) dispose d'une autorité de suggestion de recherche.

<?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>

L'autorité doit être la même dans AndroidManifest.xml, searchable.xml et votre fournisseur de contenu.

Créez un menu d'options pour votre ActionBar qui inclut un 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>

Configurez votre activité avec une SearchView associée à votre configuration consultable /

@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;
}

Quelques documents clés sont:

Ajout de suggestions personnalisées: http://developer.Android.com/guide/topics/search/adding-custom-suggestions.html

Création d'un fournisseur de contenu: http://developer.Android.com/guide/topics/providers/content-provider-creating.html

Utilisation d'un widget de recherche: http://developer.Android.com/guide/topics/search/search-dialog.html#UsingSearchWidget

50
saxman