fc2ブログ

QuickSearchBox対応の仕方

QuickSearchBox対応
実は、結構はまったのでまとめ。
物理キーや特定のボタンによる、QuickSearchBoxの表示と、
その条件による検索と、
Suggestionに分けて考える必要があります。

これを一気に考えると面倒なことになりますし、意味が不明になります。
特に、searchable.xml内の要素と、検索Action内の挙動、
ContentProviderへの繋ぎを考える必要が厄介でした。

というわけで、順繰りに見ていきます。
その1:QuickSearchBoxの表示と入力の受取
Activityを2つ用意します。
1)SearchableActivity
2)ResultActivity

アプリを立ち上げるとSearchableActivityが立ち上がり、そこではQuickSearchBoxが起動でき、そこから検索するとSearchResultが表示される。といった想定です。絞込みなんかの場合はSearchableActivityのみという構成も可能です。



<activity
android:label="@string/app_name"
android:name=".SearchableActivity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.default_searchable"
android:value=".ResultActivity" />

</activity>
<activity
android:label="SearchResult"
android:name=".ResultActivity" >
<intent-filter >
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />

</activity>

※このQuickSearchBoxはSearchableActivityのみから起動されるので、赤字のmeta-dataはSearchableActivityの下にありますが、全てのActivityを対象にしたければ、applicationタグの下に配置します。

res/xml/searchable.xml

<searchable
xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
android:imeOptions="actionSearch"
android:inputType="text"/>



ここでlabelやらhintにとりあえず・・なんて思って直接文字埋め込みとかすると、エラーを吐かずに検索ボックスが表示されないというはまりを見せるので要注意。
また、searchSuggestなんかが入っちゃってるサンプルなんかもわかりにくい原因になるのでこの段階では無視


public class SearchableActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}


public class ResultActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Bundle extras = intent.getExtras();

Log.d("QSB Sample", intent.toString());
Set keySet = extras.keySet();
for (String key : keySet) {
Log.d("QSB Sample", key + ":" + extras.getString(key));
}
}
}



ここで、検索ボタンを押下して、”検索”と入力して検索をかけると、以下ログが確認できます。
D/QSB Sample(2220): Intent { act=android.intent.action.SEARCH flg=0x10000000 cmp=com.dnsdojo.mokkouyou.android.searchable/.ResultActivity (has extras) }
D/QSB Sample(2407): user_query:検索
D/QSB Sample(2407): query:検索

※蛇足ですが、検索ボタンがないような端末を考慮して機能の入り口を作るのを忘れずに。処理は「onSearchRequested();」と呼んでやるだけです。


というわけで、検索処理なんかは

if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
doSearch(query);
}


のように、Actionを見て、クエリを取得して検索をしてやります。

ここでまとめ1。
・QuickSearchBoxを表示するのは簡単。
・searchable.xmlではちゃんと文字列リソースを利用してやること。
・検索実行時にはandroid.intent.action.SEARCHが飛んでくる。
・入力値はqueryと(user_query)で飛んでくるので、intentから取得してやり、それを利用して「結果画面」を表示してやればよい。
・検索としては別にContentProviderの仕組みは不要



後は色々、Tips的なものがありますので
http://developer.android.com/guide/topics/search/search-dialog.html
を参考にしてください。


その2:カスタムのSuggestionを提供する。

続いては、候補の表示(Suggestion)になります。今度は検索のActivityの事は忘れて下さい。
※なにげにActivityを忘れる事が重要

searchable.xmlに以下を追加します。細かいことは後々。一旦ミニマムで。

android:searchSuggestAuthority="com.dnsdojo.mokkouyou.android.searchable"
android:searchSuggestSelection=" ?"
android:searchSuggestThreshold="1"



ContentProviderを用意します。

今回はあえてDBではなく、SDカード内を検索してファイルの候補を提示する想定ですがContentProviderは仕組みとして必要です。※ここ重要

とりあえずは、queryまで必要で、後のinsertなんかは不要と思っていて結構です。

public class SuggestProvider extends ContentProvider {
public static final String FILE_MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.com.dnsdojo.mokkouyou.android.searchable.file";

@Override
public String getType(Uri uri) {
return FILE_MIME_TYPE;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
if (selectionArgs == null) {
throw new IllegalArgumentException(
"selectionArgs must be provided for the Uri: " + uri);
}
Log.d("QSB Sample", uri.toString());
Log.d("QSB Sample", selectionArgs[0]);
return null;
}

@Override
public boolean onCreate() {
return false;
}
@Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
return 0;
}
@Override
public Uri insert(Uri arg0, ContentValues arg1) {
return null;
}
@Override
public int delete(Uri arg0, String arg1, String[] arg2) {
return 0;
}
}



Manidestに以下ContentProviderの定義を追加します。

<provider android:name=".SuggestProvider"
android:authorities="com.dnsdojo.mokkouyou.android.searchable"
android:syncable="false" />




この状態で、QuickSearchBoxにて入力を行うと
D/QSB Sample(2765): content://com.dnsdojo.mokkouyou.android.searchable/search_suggest_query?limit=50
D/QSB Sample(2765): いい

のようにログが確認できると思います。

まとめ2
・Suggestionの場合は、Activityを経由しないで直接ContentProviderにクエリで要求される。
・入力された文字列はSelectionArgsに含まれる※


※実際にはandroid:searchSuggestSelection=" ?"
の指定がある為にこのような渡され方をします。

もしandroid:searchSuggestSelectionの指定をせずに、Suggestを行うと、以下のようなURIが渡され、selectionArgsはnullとなります。
D/QSB Sample(2909): content://com.dnsdojo.mokkouyou.android.searchable/search_suggest_query/%E3%81%82%E3%81%84?limit=50


その3:実際のSuggestionを表示
とまぁLogだけで確認するのも味気ないので、ダミーのCursorを返してやるものとします。
ここではDBを利用しないのでMatrixCursorを生成して返してやります。
(Sqliteのインメモリとかでもいいのかもしれませんけどね)

BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1がカラムとしては必須ですので、ひとまずはこんな感じで。

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Log.d("QSB Sample", uri.toString());
if (selectionArgs == null) {
throw new IllegalArgumentException(
"selectionArgs must be provided for the Uri: " + uri);
}
Log.d("QSB Sample", selectionArgs[0]);
MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1});
cursor.addRow(new Object[]{Integer.valueOf(1), selectionArgs[0] + "111111"});
cursor.addRow(new Object[]{Integer.valueOf(2), selectionArgs[0] + "222222"});
return cursor;
}




と、Suggestが表示されたと思います。
suggest1.jpg


で、このSuggestを選択すると
D/QSB Sample(3364): Intent { act=android.intent.action.SEARCH flg=0x10000000 cmp=com.dnsdojo.mokkouyou.android.searchable/.ResultActivity (has extras) }
D/QSB Sample(3364): user_query:だ


といままで忘れていたActivityからのログがここでようやく確認できます。


ただ、これではせっかくのSuggestが無意味ですので、選択された場合の挙動を追加したいと思います。
searchable.xmlに以下を追加します。

android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSuggestIntentData="content://com.dnsdojo.mokkouyou.android.searchable/file"




であらためてSuggestを選択すると
D/QSB Sample(3555): Intent { act=android.intent.action.VIEW dat=content://com.dnsdojo.mokkouyou.android.searchable/file flg=0x10000000 cmp=com.dnsdojo.mokkouyou.android.searchable/.ResultActivity (has extras) }
D/QSB Sample(3555): user_query:だ

といったようにActionが指定したaction.VIEWだし、dataとして指定したUriが取得できる事が確認できます。


ここまでくればあと一歩。このままじゃどの候補を選択されたのか?がわかりません。てっきり_IDを渡してくれるのかと思ったのですが、そこは細工が必要。
MatrixCursorに”SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID”を追加してやります。
MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID});
cursor.addRow(new Object[]{Integer.valueOf(1), selectionArgs[0] + "111111", Integer.valueOf(1)});
cursor.addRow(new Object[]{Integer.valueOf(2), selectionArgs[0] + "222222", Integer.valueOf(2)});


そうすると、Uriが見事にID指定の形でわたってくるようになります。
D/QSB Sample(3730): Intent { act=android.intent.action.VIEW dat=content://com.dnsdojo.mokkouyou.android.searchable/file/2 flg=0x10000000 cmp=com.dnsdojo.mokkouyou.android.searchable/.ResultActivity (has extras) }


あとはContentProviderとの間の連携ですね。そこは業務ロジックでどうするか次第。


ところで、今回はIDなんて暫定的に振ってますし、ファイルの検索なので、そもそものパスなんかが知りたい。
そんな場合ですが”SearchManager.SUGGEST_COLUMN_INTENT_DATA”を追加してやります。
MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, SearchManager.SUGGEST_COLUMN_INTENT_DATA});
cursor.addRow(new Object[]{Integer.valueOf(1), selectionArgs[0] + "111111", Integer.valueOf(1),"/mnt/sdcard/1"});
cursor.addRow(new Object[]{Integer.valueOf(2), selectionArgs[0] + "222222", Integer.valueOf(2),"/mnt/sdcard/2"});


そうするとこんどはdatをその値で上書きして渡してくれるようになります。
D/QSB Sample(3958): Intent { act=android.intent.action.VIEW dat=/mnt/sdcard/2/2 flg=0x10000000 cmp=com.dnsdojo.mokkouyou.android.searchable/.ResultActivity (has extras) }


で、ログを見てわかると思いますが、DATA_IDとDATA両方存在してしまっているので両方の処理を行っちゃってますので、DATAだけでいい場合、DATA_IDは指定する必要がありません。
また、このINTENT_DATAを直接指定するようなケースではandroid:searchSuggestIntentDataの指定も不要です。

その辺は用途によって使い分ける必要がありますね。


また意図的に後回しにしていましたが、
ResultActivityでは、android.intent.action.VIEWで受け取った場合、Suggestを受けたといった処理に分岐してやればよいです。
※検索を受けた場合はandroid.intent.action.SEARCHで分岐


最後のまとめ。
・QuickSearchBoxの仕組みとしてはContentProviderは不要。(実際の検索としては必要かもしれないけど)
・Suggestionの仕組みとしては、DB関係なくてもContentProviderは必要
・searchable.xmlの内容はよく調査する事


検索の場合、Activityが呼ばれる
Suggest指定の場合ContentProviderが直接呼ばれる
Suggestを選択した場合、Activityが呼ばれる

で、それぞれ気の利いたことはsearchableとContentProviderなんかで指定が出来ます。と。

長くなりましたが、これでおしまい。
最後にsearchable.xml
http://developer.android.com/guide/topics/search/searchable-config.html
関連記事

コメントの投稿

非公開コメント

管理人のみ閲覧できます

このコメントは管理人のみ閲覧できます
スポンサードリンク
開発中に一息

アプリ一覧
最新記事
最新コメント
最新トラックバック
検索フォーム
月別アーカイブ
カテゴリ
RSSリンクの表示
リンク
QRコード
QR