2012-07-15 22 views
27

Tengo un AutoCompleteTextView, que da al usuario la finalización automática de resultados de búsqueda de Google Places API . Una vez que terminé descubrí SearchView y cómo se puede colocar en ActionBar. Revisé el ejemplo de SearchView provisto por google y lo agregué a mi aplicación como punto de partida (enumera las aplicaciones instaladas) pero no sé cómo proceder desde aquí. Quiero tener la misma funcionalidad que AutoCompleteTextView, pero en su lugar uso SearchView. ¿Alguna sugerencia? Toda la clase se proporciona a continuación.AutoCompleteTextView en un SearchView en la Barra de acciones en lugar

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

    } 

} 

Después de actualizar el código sugerido por saxman, puedo ver que el método de consulta en el proveedor nunca es llamado:

Mi archivo de manifiesto es el siguiente:

<?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 la misma autoridad en el manifiesto, el proveedor de contenido y el archivo de manifiesto. Veo searchView en el menú y no he modificado el método de consulta, así que debería devolver el cursor de una línea. pero el método de consulta nunca se llama. por favor ayuda. ¡Otro problema que acabo de descubrir es que el searchView no muestra el search_hint especificado!

Proporcionar más código * 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(); 
    } 
} 
+0

Hola, pregunto, ¿hay alguna ventaja para el uso de SearchView sobre AutoCompleteTextView para la funcionalidad de búsqueda en la barra de acción? –

+0

Excelente enlace para futuros servidores; http://www.drzon.net/how-to-create-a-clearable-autocomplete-dropdown-with-autocompletetextview/ – Tina

Respuesta

48

llegar a diferentes lugares Autocompletar resultados de la API en un SearchView, primero necesitará un ContentProvider para la 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(); 
    } 
} 

A continuación, agregue el código de cliente API Lugares de autocompletar en el consulta método del proveedor de contenido. Extraer la entrada del usuario de la siguiente manera:

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

Añadir la PlacesSuggestionProvider a su AndroidManifest, y asegúrese de que su actividad tiene una configuración de búsqueda.

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

Y asegúrese de que su configuración de búsqueda (res/xml/searchable.xml) tiene una búsqueda sugiere autoridad.

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

La autoridad debe ser el mismo en AndroidManifest.xml, searchable.xml, y su proveedor de contenidos.

Cree un menú de opciones para su ActionBar que incluya 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> 

Configure su actividad con un SearchView que está asociada con la configuración de búsqueda/

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

Unos documentos clave son:

Adición Sugerencias personalizadas: http://developer.android.com/guide/topics/search/adding-custom-suggestions.html

Creación de un contenido Proveedor: http://developer.android.com/guide/topics/providers/content-provider-creating.html

El uso de un widget de búsqueda: http://developer.android.com/guide/topics/search/search-dialog.html#UsingSearchWidget

+0

gracias por la respuesta. ¡Entonces el proveedor de contenido buscará en Google Places y devolverá un cursor! ¿Pero todavía no sé cómo se conecta la búsqueda ViewView? – theAlse

+0

Agregó ese código también (los últimos 2 bloques de código). En onCreateOptionsMenu, configura el SearchView para usar la configuración de búsqueda que definió para su actividad. – saxman

+0

por favor vea la pregunta actualizada con los errores que obtuve! – theAlse

Cuestiones relacionadas