為Android應(yīng)用增加搜索功能:增加搜索建議
譯文在上一篇教程<<為Android應(yīng)用添加搜索功能>>中,我們學(xué)習(xí)了如何在android應(yīng)用中其搜索框架的基本功能,在本講中,將繼續(xù)學(xué)習(xí)在搜索應(yīng)用中增加搜索提示建議的功能。
什么是搜索建議
首先我們來看下什么是搜索建議。在Android應(yīng)用中,當(dāng)用戶輸入搜索字符串是,系統(tǒng)會根據(jù)輸入的字符串的部分或整體,以下拉框的方式列出含有用戶輸入字符串的列表,這樣用戶就可以不必輸入完所有字符,可以直接從下拉列表中選擇,方便了用戶。如果能在APP應(yīng)用的搜索模塊中加入這個功能,對用戶來說無疑是十分方便的,下圖是在搜索應(yīng)用中使用搜索建議的圖:
搜索建議的兩類模式
要讓搜索應(yīng)用支持搜索建議,必須在應(yīng)用中增加一個自定義的content provider,并且要設(shè)置配置文件中的搜索元數(shù)據(jù)。
Android支持兩種類型的搜索建議模式:
· 基于用戶輸入的搜索建議
· 基于APP應(yīng)用本身數(shù)據(jù)庫的搜索建議,即從APP應(yīng)用數(shù)據(jù)庫中提取數(shù)據(jù)做搜索建議
下面分別講解兩種搜索建議方式是如何配置的:
基于用戶輸入的搜索建議
這種方式相對比較容易,先來介紹下。在Android中,提供了SearchRecentSuggestionsProvider這個類,可以實現(xiàn)從用戶最近輸入的內(nèi)容中進(jìn)行檢索,開發(fā)者只需要做的,只需要繼承這個類,并且在構(gòu)造函數(shù)中進(jìn)行如下設(shè)置:
- import android.content.SearchRecentSuggestionsProvider;
- publicclass SampleRecentSuggestionsProvider
- extends SearchRecentSuggestionsProvider {
- public static final String AUTHORITY =
- SampleRecentSuggestionsProvider.class.getName();
- public static final int MODE =DATABASE_MODE_QUERIES;
- public SampleRecentSuggestionsProvider() {
- setupSuggestions(AUTHORITY, MODE);
- }
- }
在上面的這個類中,繼承了SearchRecentSuggestionsProvider,并且在構(gòu)造函數(shù)中進(jìn)行了設(shè)置,這樣這個自定義的contentprovider就擁有了查詢用戶最近輸入檢索的能力了。
但必須同時保存用戶曾經(jīng)的檢索輸入,這樣就能在用戶再次輸入時,重新顯示出來,這個必須在search的activity中,寫入如下代碼:
- SearchRecentSuggestions suggestions =
- newSearchRecentSuggestions(this,
- SampleRecentSuggestionsProvider.AUTHORITY,
- SampleRecentSuggestionsProvider.MODE);
- suggestions.saveRecentQuery(query, null);
上面的代碼中可以看到,使用了SearchRecentSuggestionsProvider的
saveRecentQuery方法進(jìn)行了搜索記錄的保存。
接下來,在配置文件中必須對這個contentprovider進(jìn)行配置,如下:
- <provider
- android:authorities="de.openminds.SampleRecentSuggestionsProvider"
- android:name=".SampleRecentSuggestionsProvider">
- </provider>
***,在searchable.xml中進(jìn)行配置即可使用,如下:
- <? xml version="1.0"encoding="utf-8"?>
- <searchablexmlns:androidsearchablexmlns:android="http://schemas.android.com/apk/res/android"
- android:label="@string/search_label"
- android:hint="@string/search_hint"
- android:searchSettingsDescription="@string/search_settings_description"
- android:searchSuggestAuthority="com.grokkingandroid.SampleRecentSuggestionsProvider "
- android:searchSuggestIntentAction="android.intent.action.SEARCH"
- android:searchSuggestThreshold="1"
- android:includeInGlobalSearch="true"
- android:searchSuggestSelection=" ?"
- >
- </searchable>
其中的一些參數(shù)說明如下:
android:searchSuggestAuthorith
此屬性的值就是SearchSuggestAuthorith中的AUTHORITH了。
android:searchSuggestIntentAction
此屬性定義了當(dāng)我們選中搜索提示的內(nèi)容時發(fā)生的目的動作。
android:searchSuggestThreshold
此屬性定義了至少輸入幾個字符時才會彈出提示
android:includeInGlobalSearch
是否將內(nèi)容加入android的全局搜索。true,加入。
android:searchSuggestSelection
定義搜索時參數(shù)的占位符
Android中數(shù)據(jù)庫保存的搜索記錄
SearchRecentSuggestionsProvider將用戶最近的搜索記錄保存在suggestions.db中,這個db保存了id,query查詢項,一個顯示的文本和搜索時候的時間戳。注意的是這個時間戳是必須的,因為所有的搜索記錄都是按時間排序的。
作為開發(fā)者來說,應(yīng)該考慮定期清理搜索記錄,或者提供給用戶手動清除搜索記錄的機(jī)會,以讓用戶定期從搜索建議列表中看到更多的搜索建議項。清除搜索建議記錄是十分容易的,只需要調(diào)用SearchRecentSuggestions對象的clearHistory()方法即可。Android會默認(rèn)保留250個搜索建議項在系統(tǒng)中。
基于應(yīng)用的搜索建議
在更多情況下,開發(fā)者希望是提供給用戶基于app應(yīng)用本身的搜索功能,這樣用戶在搜索時,將基于APP應(yīng)用本身的內(nèi)容提供給用戶搜索建議。這個時候,在自定義provider中,只需要關(guān)注query()和getContentType()就可以了。
首先,在應(yīng)用中必須獲得用戶在搜索文本框中輸入的內(nèi)容,然后再調(diào)用query()方法去搜索app應(yīng)用。這個可以通過如下兩類方法去獲得用戶的輸入內(nèi)容
· 默認(rèn)的是通過URI的方式獲得用戶的輸入內(nèi)容
· 在配置文件中的查詢字符串
先看下如何過URI的方式獲得用戶的輸入內(nèi)容。系統(tǒng)都會通過query()函數(shù)在Content Provider中進(jìn)行查詢,然后用Cursor返回對應(yīng)的suggestion。系統(tǒng)對用戶的輸入,構(gòu)造成如下的URI:
content://authority/optionalPath/SUGGEST_URI_PATH_QUERY /queryText
注意這里的Uri末尾的querytext是用URL方式進(jìn)行編碼的,所以你需要解碼。
一般都是采用如下的方式取得它:Stringquery = uri.getLastPathSegment().toLowerCase();
這里的optional.suggest.path就是在searchable配置文件中設(shè)置的android:searchSuggestPath,如果在searchable配置文件沒有設(shè)置它,optional.suggest.path當(dāng)然也不會包括在uri中。只有需要一個ContentProvider為多個searchable activities提供suggestions查詢的時候,才需要設(shè)置android:searchSuggestPath,這時它用于區(qū)分是哪個searchableactivities。
注意:SUGGEST_URI_PATH_QUERY 并不是URI的字面字符串,它是一個靜態(tài)成員常量,表示是把該常量的值加到uri中
下面是相關(guān)的使用方法代碼:
- public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder){
- String query = uri.getLastPathSegment();
- if (SearchManager.SUGGEST_URI_PATH_QUERY.equals(query)) {
- //如果找到符合用戶輸入的記錄
- }
- else {
- //如果找不到符合用戶輸入的記錄
- }
- }
接著看下如何在配置文件中進(jìn)行查詢字符串的設(shè)置。可以在配置文件中進(jìn)行如下配置,增加android:searchSuggestSelection選項,如下:
android:searchSuggestSelection="namelike ?"
那么query()方法代碼如下:
- public Cursor query(Uri uri, String[]projection, String selection,
- String[]selectionArgs, String sortOrder) {
- if (selectionArgs!= null && selectionArgs.length > 0 &&selectionArgs[0].length() > 0) {
- // 用戶輸入內(nèi)容保存在 selectionArgs[0] 中
- }
- else {
- // 用戶沒輸入任何內(nèi)容
- }
- }
此外,還可以在配置文件中配置搜索建議的最短字符數(shù),比如android:searchSuggestThreshold="3",則表明,用戶至少輸入3個字符后,才開始調(diào)用搜索建議項。
處理返回的結(jié)果
下面講解如何處理搜索建議返回給前端界面的問題。首先要在配置文件中,配置使用哪個action并且使用哪一個URI(這個URI其實就是搜索建議項的***數(shù)據(jù)來源列表),配置如下:
android:searchSuggestIntentAction =
"android.intent.action.VIEW" android:searchSuggestIntentData = "content://someAuthority/somePath" |
對返回的搜索建議項,是以一個cursor的方式返回的,各個列必須嚴(yán)格遵守相關(guān)的規(guī)定,下面是幾個重要的列的列表:
常量 |
用法 |
SUGGEST_COLUMN_TEXT_1 |
要在***行顯示的文本 |
SUGGEST_COLUMN_TEXT_2 |
在第二行顯示的文字 |
SUGGEST_COLUMN_INTENT_DATA_ID |
添加到data_uri的額外的id |
雖然說只有Searchmanager.SUGGEST_COLUMN_TEXT_1是強(qiáng)制要求的,但由于要知道用戶選擇的是哪一個選項,因此一般來說,id選項也是需要的。
此外,還需要將查詢出來的搜索數(shù)據(jù)轉(zhuǎn)換為cursor的數(shù)據(jù)格式,這個需要進(jìn)行一點轉(zhuǎn)換工作。這可以使用SQLITEQueryBuilder的setprojectionMap方法進(jìn)行轉(zhuǎn)換,在下一篇教程中,將會具體講解其用法,這里先列出相關(guān)代碼如下:
- Map<String, String> projectionMap = newHashMap<String, String>();
- projectionMap.put(COL_BAND, COL_BAND +" AS " + SearchManager.SUGGEST_COLUMN_TEXT_1);
- projectionMap.put(COL_ID, COL_ID);
- projectionMap.put(COL_ROW_ID,COL_ROW_ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
- projectionMap.put(COL_LOCATION,COL_LOCATION + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_2);
- projectionMap.put(COL_DATE, COL_DATE);
- builder.setProjectionMap(projectionMap);
響應(yīng)用戶搜索的intent
當(dāng)用戶輸入搜索詞后,Android的搜索框架將調(diào)用manifest文件中配置的搜索的activity,這其中會使用顯式的intent去實現(xiàn)。就在app中搜索用的搜索建議而言,這個intent會包括在配置文件中的數(shù)據(jù)和從cursor中返回的數(shù)據(jù)。
對用戶搜索的響應(yīng)的具體處理,取決于使用的是基于用戶已經(jīng)輸入過的搜索詞的搜索建議,還是基于APP應(yīng)用本身數(shù)據(jù)提供的搜索建議。
如果使用了基于用戶已經(jīng)輸入過的搜索詞的搜索建議,那么intent的action就是Intent.ACTION_SEARCH ,這和上一篇教程中談到的是一樣的。
對于基于APP應(yīng)用本身數(shù)據(jù)提供的搜索建議的情況,用戶大多數(shù)情況下,希望看到的是數(shù)據(jù)的詳細(xì)的情況,所以點了提供的搜索建議后,期望跳轉(zhuǎn)看到數(shù)據(jù)的詳情,這個可以通過設(shè)置Intent.ACTION_VIEW來實現(xiàn)。
由于搜索的activity一般會繼承ListActivity,所以一般情況下需要另外打開一個activity去查看某項數(shù)據(jù)的具體內(nèi)容,代碼如下:
- private void handleIntent(Intentintent) {
- if(Intent.ACTION_SEARCH.equals(intent.getAction())) {
- String query =intent.getStringExtra(SearchManager.QUERY);
- doSearch(query);
- } else if(Intent.ACTION_VIEW.equals(intent.getAction())) {
- Uri detailUri =intent.getData();
- String id = detailUri.getLastPathSegment();
- Intent detailsIntent =new Intent(getApplicationContext(), DetailsActivity.class);
- detailsIntent.putExtra("ID", id);
- startActivity(detailsIntent);
- finish();
- }
- }
全局搜索
對于全局搜索的設(shè)置,只需要在配置文件中進(jìn)行如下設(shè)置:
android:includeInGlobalSearch="true"
但問題是如何將你的APP應(yīng)用能放置到Android本身的搜索列表中去呢?如下圖:
你的APP應(yīng)用本身不能改變這些值,但Android提供了
android.app.SearchManager.INTENT_ACTION_GLOBAL_SEARCH
的方法,可以將你的APP應(yīng)用添加到搜索列表中去,這樣的話,在搜索列表中,可以象如下圖的樣子進(jìn)行搜索:
關(guān)于搜索建議***的問題
要注意的是關(guān)于搜索建議***的問題,在于開發(fā)者很難在樣式上進(jìn)行控制,這將會是一個很大的問題。特別對于APP中如果搜索建議展示的樣式跟APP中其他的樣式很不同的話,將會影響用戶體驗。
另外,如果要在搜索建議中展示不同的數(shù)據(jù) ,有的時候也會變得麻煩。比如在一個關(guān)于音樂會的app應(yīng)用中,可能會出現(xiàn)位置和樂團(tuán)或樂隊的相關(guān)信息,開發(fā)者企圖將它們在搜索建議中明確劃分開來,以方便用戶選擇,但可惜除非開發(fā)者進(jìn)行自定義開發(fā),否則無法實現(xiàn)這樣的功能。
小結(jié)
在本篇教程中,重點講解了如何在APP應(yīng)用中加入搜索建議的兩種方法和注意點,在下一篇教程中,將講解如何使用搜索的快捷方式,敬請期待。
【51CTO譯稿,非經(jīng)授權(quán)謝絕轉(zhuǎn)載,合作媒體轉(zhuǎn)載請注明原文出處、作者及51CTO譯者!】