Androidアプリ上でのサーチハードウェアキーによる振る舞い機能を実装
まだ微妙だけどメモる。一応、テストケースとかで色々してませんので
概要
というような画面があって、サーチハードウェアキーを押して「h」を入力した後に
というようなフィルタリングを行ったり
というようにサーチハードウェアキーを押してキーワードでインクレメンタル検索のようなのを実装したい場合にはSearchRecentSuggestionProvider等を駆使する事で出来る模様
ただ結果をフィルタリングする機能自体はただのAdapterのFilterを実装するだけ
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="sample.test"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<application android:icon="@drawable/ic_launcher" android:label="@string/app_name">
<activity android:name=".MainActivity" android:label="@string/app_name" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.default_searchable" android:value=".MainActivity" />
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable" />
</activity>
<provider android:name=".SearchSuggestionProvider" android:authorities="sample.test.provider" />
</application>
</manifest>
res/xml/searchable.xml
<?xml version="1.0" ?>
<searchable
xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:searchSuggestAuthority="sample.test.provider"
android:searchSuggestSelection=" ? "
android:searchSuggestIntentAction="sample.test.action.SEARCH_SUGGEST" />
SampleArrayAdapter.java
ListViewの結果な部分をフィルタするのに必要なArrayAdapter
package sample.test;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;
import android.content.Context;
import android.widget.ArrayAdapter;
import android.widget.Filter;
public class SampleArrayAdapter extends ArrayAdapter<String> {
private Filter filter;
private List<String> items = new ArrayList<String>();
public SampleArrayAdapter(Context ctx) {
super(ctx, android.R.layout.simple_list_item_1);
String[] items = ctx.getResources().getStringArray(R.array.values);
// API Level11からならaddAllメソッドで対応可能かも
for (String item : items) {
add(item);
this.items.add(item);
}
}
@Override
public Filter getFilter() {
if (filter == null) {
filter = new SampleFilter();
}
return filter;
}
private class SampleFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
// 検索クエリーから
SortedSet<String> filterItems = new TreeSet<String>();
if (constraint != null && constraint.toString().length() > 0) {
StringTokenizer tokens = new StringTokenizer(constraint.toString());
while(tokens.hasMoreTokens()) {
String token = tokens.nextToken();
for (String item : items) {
if (item.contains(token)) {
filterItems.add(item);
}
}
}
} else {
filterItems = new TreeSet<String>(items);
}
results.values = filterItems;
results.count = filterItems.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint,FilterResults results) {
@SuppressWarnings("unchecked")
SortedSet<String> filters = (SortedSet<String>)results.values;
clear();
for (String filter : filters) {
add(filter);
}
}
}
}
SearchSuggestionProvider.java
検索項目からフィルターしてサジェストする項目をCursorで出す
package sample.test;
import android.content.SearchRecentSuggestionsProvider;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
public class SearchSuggestionProvider extends SearchRecentSuggestionsProvider {
public SearchSuggestionProvider() {
setupSuggestions("sample.test.provider", SearchSuggestionProvider.DATABASE_MODE_QUERIES);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
if (selectionArgs.length > 0) {
MatrixCursor csr = new MatrixCursor(
new String[] { "_id", "suggest_text_1", "suggest_intent_query" }
);
String[] values = getContext().getResources().getStringArray(R.array.values);
String query = selectionArgs[0];
String[] queries = query.split("s");
for (int i = 0;i < values.length;i++) {
String value = values[i];
for (String splitedQuery : queries) {
if (value.contains(splitedQuery)) {
csr.addRow(new Object[] { i, value, value });
}
}
}
return csr;
}
return super.query(uri,projection,selection,selectionArgs,sortOrder);
}
}
MainActivity.java
package sample.test;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Filter;
import android.widget.Toast;
import static android.app.SearchManager.QUERY;
import static android.app.SearchManager.APP_DATA;
public class MainActivity extends ListActivity {
public static final String ACTION_SEARCH_SUGGEST = "sample.test.action.SEARCH_SUGGEST";
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
getListView().setTextFilterEnabled(true);
setListAdapter(new SampleArrayAdapter(this));
// onSearchRequested();
}
@Override
public void onNewIntent(Intent intent) {
String action = intent.getAction();
// startSearch際に指定されたBundleを取得できる模様
Bundle bundle = intent.getBundleExtra(APP_DATA);
if (Intent.ACTION_SEARCH.equals(action)) {
final Filter textFilter = ((SampleArrayAdapter)getListAdapter()).getFilter();
textFilter.filter(intent.getStringExtra(QUERY), new Filter.FilterListener() {
public void onFilterComplete(int count) {
if (count <= 0) {
// もしマッチせず見つからなかった場合にListAdapterにバインドされてるフィルターを無効にしないと元のデータ一覧が表示されなくなる
textFilter.filter(null);
}
}
});
} else if (ACTION_SEARCH_SUGGEST.equals(action)) {
Bundle b = intent.getExtras();
// サーチハードウェア上の項目をクリックした際の項目はsuggest_intent_queryから取れる模様
String selectedValue = b.getString(QUERY);
if (selectedValue != null) {
Toast.makeText(this, "selected: " + selectedValue, Toast.LENGTH_LONG).show();
}
}
}
@Override
public boolean onSearchRequested() {
Bundle bundle = new Bundle();
bundle.putString("message", "hoge fuga foobar");
startSearch(null, false, bundle, false);
return true;
}
}
ACTION_SEARCHはサーチハードウェアを押してキーワードを入力して検索した際に、ACTION_SEARCH_SUGGEST(searchabel.xmlで設定したIntentAction)は検索候補をタッチした際に返ってくる模様
んまぁざっくり書いたけど、 http://y-anz-m.blogspot.jp/2010/03/android-searchmanager.html が参考になるんじゃねーかなって所