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