(EditText を extends してるのに AutoCompleteEditText じゃないのはなぜなんだ)
こんな感じで候補の一覧を Adapter として用意して AutoCompleteTextView の setAdapter() でセットします。
public class CountriesActivity extends Activity {
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.countries);
AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.countries_list);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, COUNTRIES);
textView.setAdapter(adapter);
}
private static final String[] COUNTRIES = new String[] {
"Belgium", "France", "Italy", "Germany", "Spain"
};
}
過去の入力を候補をして表示したい場合、候補が随時かわるので上記の方法は使えません。
そこで、SearchManager の出番です。
SearchManager については以前のエントリ「Y.A.M の雑記帳 - Android SearchManager 検索ボックスを使うぜ!」あたりをみてください。
簡単にいうと、SearchManager から候補一覧の Cursor(候補は ContentProvider として提供される)を取得して、それを Adapter にセットすればいいわけです。
SearchManager から候補一覧を取得するには SearchManager の getSuggestions() を呼べばいいのですが、残念ながら @hide です。 ただ、SearchableInfo があれば同じことはできます。
シンプルな Adapter だとだいたいこんな感じです。
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.v4.widget.ResourceCursorAdapter;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
class SearchRecentSuggestionsAdapter extends ResourceCursorAdapter {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "SearchRecentSuggestionsAdapter";
private static final int QUERY_LIMIT = 50;
private SearchableInfo mSearchable;
private boolean mClosed = false;
static final int INVALID_INDEX = -1;
private int mText1Col = INVALID_INDEX;
public SearchRecentSuggestionsAdapter(Context context, SearchableInfo searchable) {
super(context, R.layout.search_recent_row, null, true);
mSearchable = searchable;
}
/**
* Overridden to always return false
, since we cannot be sure
* that suggestion sources return stable IDs.
*/
@Override
public boolean hasStableIds() {
return false;
}
/**
* Use the search suggestions provider to obtain a live cursor. This will be
* called in a worker thread, so it's OK if the query is slow (e.g. round
* trip for suggestions). The results will be processed in the UI thread and
* changeCursor() will be called.
*/
@Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (DEBUG)
Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")");
String query = (constraint == null) ? "" : constraint.toString();
Cursor cursor = null;
try {
cursor = getSuggestions(mSearchable, query, QUERY_LIMIT);
return cursor;
} catch (RuntimeException e) {
Log.w(LOG_TAG, "Search suggestions query threw an exception.", e);
}
return null;
}
public Cursor getSuggestions(SearchableInfo searchable, String query, int limit) {
if (searchable == null) {
return null;
}
String authority = searchable.getSuggestAuthority();
if (authority == null) {
return null;
}
Uri.Builder uriBuilder = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority);
// if content path provided, insert it now
final String contentPath = searchable.getSuggestPath();
if (contentPath != null) {
uriBuilder.appendEncodedPath(contentPath);
}
// append standard suggestion query path
uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
// get the query selection, may be null
String selection = searchable.getSuggestSelection();
// inject query, either as selection args or inline
String[] selArgs = null;
if (selection != null) {
// use selection if provided
selArgs = new String[] { query };
} else {
// no selection, use REST pattern
uriBuilder.appendPath(query);
}
if (limit > 0) {
uriBuilder.appendQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT, String.valueOf(limit));
}
Uri uri = uriBuilder.build();
return mContext.getContentResolver().query(uri, null, selection, selArgs, null);
}
public void close() {
if (DEBUG)
Log.d(LOG_TAG, "close()");
changeCursor(null);
mClosed = true;
}
/**
* Cache columns.
*/
@Override
public void changeCursor(Cursor c) {
if (DEBUG)
Log.d(LOG_TAG, "changeCursor(" + c + ")");
if (mClosed) {
Log.w(LOG_TAG, "Tried to change cursor after adapter was closed.");
if (c != null)
c.close();
return;
}
try {
super.changeCursor(c);
if (c != null) {
mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
}
} catch (Exception e) {
Log.e(LOG_TAG, "error changing cursor and caching columns", e);
}
}
/**
* Tags the view with cached child view look-ups.
*/
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = super.newView(context, cursor, parent);
v.setTag(new ViewHolder(v));
return v;
}
/**
* Cache of the child views of drop-drown list items, to avoid looking up
* the children each time the contents of a list item are changed.
*/
private final static class ViewHolder {
public final TextView mText1;
public ViewHolder(View v) {
mText1 = (TextView) v.findViewById(android.R.id.text1);
}
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder views = (ViewHolder) view.getTag();
if (views.mText1 != null) {
String text1 = getStringOrNull(cursor, mText1Col);
views.mText1.setText(text1);
views.mText1.setVisibility(TextUtils.isEmpty(text1) ? View.GONE : View.VISIBLE);
}
}
/**
* Gets the text to show in the query field when a suggestion is selected.
*
* @param cursor
* The Cursor to read the suggestion data from. The Cursor should
* already be moved to the suggestion that is to be read from.
* @return The text to show, or null
if the query should not be
* changed when selecting this suggestion.
*/
@Override
public CharSequence convertToString(Cursor cursor) {
if (cursor == null) {
return null;
}
String query = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_QUERY);
if (query != null) {
return query;
}
if (mSearchable.shouldRewriteQueryFromData()) {
String data = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
if (data != null) {
return data;
}
}
if (mSearchable.shouldRewriteQueryFromText()) {
String text1 = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_TEXT_1);
if (text1 != null) {
return text1;
}
}
return null;
}
/**
* This method is overridden purely to provide a bit of protection against
* flaky content providers.
*
* @see android.widget.ListAdapter#getView(int, View, ViewGroup)
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
return super.getView(position, convertView, parent);
} catch (RuntimeException e) {
Log.w(LOG_TAG, "Search suggestions cursor threw exception.", e);
// Put exception string in item title
View v = newView(mContext, mCursor, parent);
if (v != null) {
ViewHolder views = (ViewHolder) v.getTag();
TextView tv = views.mText1;
tv.setText(e.toString());
}
return v;
}
}
public static String getColumnString(Cursor cursor, String columnName) {
int col = cursor.getColumnIndex(columnName);
return getStringOrNull(cursor, col);
}
private static String getStringOrNull(Cursor cursor, int col) {
if (col == INVALID_INDEX) {
return null;
}
try {
return cursor.getString(col);
} catch (Exception e) {
Log.e(LOG_TAG, "unexpected error retrieving valid column from cursor, " + "did the remote process die?", e);
return null;
}
}
}
search_recent_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="8dip"
android:layout_width="match_parent"
android:layout_height="48dip" >
<TextView android:id="@android:id/text1"
style="?android:attr/dropDownItemStyle"
android:textAppearance="?android:attr/textAppearanceSearchResultTitle"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true" />
</RelativeLayout>
この Adapter を AutoCompleteTextView にセットすれば OK です。
public class InputActivity extends Activity {
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
final AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.editText1);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
final SearchableInfo info = searchManager.getSearchableInfo(getComponentName());
textView.setThreshold(info.getSuggestThreshold());
textView.setImeOptions(info.getImeOptions());
int inputType = info.getInputType();
if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
if (info.getSuggestAuthority() != null) {
inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
}
}
textView.setInputType(inputType);
if (info.getSuggestAuthority() != null) {
// 候補の CursorAdapter をセット
SearchRecentSuggestionsAdapter adapter = new SearchRecentSuggestionsAdapter(this, info);
textView.setAdapter(adapter);
}
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 検索文字列を候補に追加
String query = textView.getText().toString();
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(InputActivity.this, info.getSuggestAuthority(),
SearchRecentSuggestionsProvider.DATABASE_MODE_QUERIES);
suggestions.saveRecentQuery(query, null);
}
});
}
}
この Activity にはもちろん AndroidManifest.xml で searchable をセットしておくことが必要です。
0 件のコメント:
コメントを投稿