AutoCompleteTextViewがあります--Google Places APIからユーザーにオートコンプリート検索結果を提供します。完了したら、SearchViewとActionBarに配置する方法を発見しました。 googleが提供するSearchViewの例をチェックアウトし、開始点としてアプリケーションに追加しました(インストールされているアプリケーションがリストされています)が、ここから先に進む方法がわかりません。 AutoCompleteTextViewと同じ機能が必要ですが、代わりにSearchViewを使用します。なにか提案を?クラス全体を以下に示します。
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());
}
}
}
saxmanによって提案されたコードを更新した後、プロバイダーのクエリメソッドが呼び出されないことがわかります:
私のマニフェストファイルは次のようになります。
<?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>
マニフェスト、コンテンツプロバイダー、およびマニフェストファイルで同じ権限を使用します。メニューにsearchViewが表示されますが、クエリメソッドを変更していないため、1行のカーソルが返されるだけです。ただし、クエリメソッドは呼び出されません。助けてください。私が見つけた別の問題は、searchViewが指定されたsearch_hintを表示しないことです!
さらにコードを提供する *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();
}
}
SearchViewでプレイスオートコンプリートAPIの結果を取得するには、最初にAPIのContentProviderが必要です。
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();
}
}
次に、プレイスオートコンプリートAPIクライアントコードをコンテンツプロバイダーのqueryメソッドに追加します。次のようにユーザー入力を抽出します。
String query = uri.getLastPathSegment().toLowerCase();
PlacesSuggestionProviderをAndroidManifestに追加し、アクティビティに検索可能な構成があることを確認します。
<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>
また、検索可能な構成(res/xml/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.example.google.places.search_suggestion_provider">
</searchable>
権限は、AndroidManifest.xml、searchable.xml、およびコンテンツプロバイダーで同じである必要があります。
SearchView(/res/menu/options_menu.xml)を含むActionBarのオプションメニューを作成します。
<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>
検索可能な構成に関連付けられているSearchViewでアクティビティを構成します/
@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;
}
いくつかの重要なドキュメントは次のとおりです。
カスタム提案の追加: http://developer.Android.com/guide/topics/search/adding-custom-suggestions.html
コンテンツプロバイダーの作成: http://developer.Android.com/guide/topics/providers/content-provider-creating.html
検索ウィジェットの使用: http://developer.Android.com/guide/topics/search/search-dialog.html#UsingSearchWidget