添加自定義建議項
Adding Custom Suggestions
在本文中
關鍵類
SearchRecentSuggestionsP
相關示例
參閱
利用Android的搜索對話框或搜索widget,可以提供自定義搜索建議項,它的數據來源於應用程序自身。比如,假設應用程序是一個字典應用,就可以把字典中最匹配已錄入文本的單詞作爲建議項。由於能有效預測用戶所查文本並能讓用戶直接獲得字典中的釋義,這種建議項是最有價值的。圖1展示了使用自定義建議項的搜索對話框示例:
圖1.使用自定義對話框的搜索對話框截屏
一旦提供了自定義建議項,就可以同時讓它應用於系統級的“快速搜索框”,從應用程序之外訪問搜索內容。
在添加自定義建議項之前,需要先在應用程序中實現一個Android搜索對話框或搜索widget。如果還沒有的話,請參閱創建搜索界面。
如果用戶選中了某個自定義建議項,Android系統將向搜索activity發送一個Intent。標準的搜索請求將會發送一個附帶ACTION_SEARCH action的intent,不過仍可讓自定義建議項使用ACTION_VIEW(或者任何其它action),並放入相應的選中建議項數據。接着上面的字典應用示例,用戶選中一個建議項時,應用程序可以立即顯示單詞的釋義,而不是去字典檢索匹配項。
爲了提供自定義建議項功能,請按以下步驟操作:
·
·
·
·
·
當搜索對話框顯示出來時,Android系統同時也會顯示搜索建議項。需要提供的只是系統能從中讀取建議項的content provider。如果對創建content provider還不很熟悉,請先閱讀Content Providers開發者指南再往下繼續吧。
一旦系統識別出activity支持搜索功能且已提供搜索建議項,用戶鍵入搜索請求時會執行以下步驟:
1.
2.
3.
顯示完畢,將發生以下事情:
·
·
·
要加入對自定義建議項的支持,請在搜索配置文件的<searchable>元素中添加android:searchSuggestAuthority屬性。例如:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
</searchable>
根據建議項相關的intent類型,以及如何組織提交給content provider的請求文本,可能還需要用到其它一些屬性。其它可選的屬性將在後續章節中討論。
創建Content Provider
要爲自定義建議項創建content provider,需要具備content provider的相關知識,這在Content Provider開發者指南中描述。自定義建議項所需的content provider絕大部分都與其它content provider相同。不過,Cursor中對應每個建議項的數據行都必須包含系統可解析的數據列,用於存儲建議項數據。
用戶開始在搜索對話框或搜索widget裏輸入文本時,每鍵入一個字符,系統都將調用一次query(),並在content provider中檢索建議項。在query()中,必須實現對content provider建議項數據的檢索,並返回一個指向最佳建議項數據行的Cursor。
關於爲自定義建議項創建content provider的詳情,將在以下章節討論:
系統如何向content provider發送請求及如何處理。
如何定義數據列,系統用於在每次搜索時返回Cursor。
當系統向content provider發送建議項檢索請求時,將會調用content provider的query()方法。必須在該方法中實現搜索建議項數據並返回指向最匹配建議項的Cursor。
以下是系統傳給query()方法的所有參數(按照調用順序排列):
uri
通常是content Uri,格式如下:
content://your.authority/optional.suggest.path/SUGGEST_URI_PATH_QUERY
系統的默認行爲是傳遞URI並後跟搜索請求文本。例如:
content://your.authority/optional.suggest.path/SUGGEST_URI_PATH_QUERY/puppies
末尾的請求文本是用URI 編碼規則編碼過的,因此可能要在執行搜索前進行解碼。
僅當搜索配置文件中用android:searchSuggestPath屬性設置了路徑時,URI中才要包含optional.suggest.path部分。只有多個搜索activity需共用同一個content provider時,才需要用到該配置。在這種情況下,需要解析建議項請求的來源。
注意:SUGGEST_URI_PATH_QUERY 並不屬於URI的一部分,而應是用於指向此路徑的常量。
projection
總爲null
selection
該值由搜索配置文件中的android:searchSuggestSelection屬性提供,如果未聲明android:searchSuggestSelection屬性則爲null。更多使用信息請參見下文讀取請求 。
selectionArgs
如果已在搜索配置文件中聲明瞭android:searchSuggestSelection屬性,那麼數組的第一個(也只有一個)元素包含了搜索請求。如果未聲明android:searchSuggestSelection,則本參數將爲null。更多使用信息請參見下文讀取請求 。
sortOrder
總爲null。
系統可以用兩種方式發送搜索請求文本。默認的方式是作爲content URI路徑的末尾部分(last segment)在uri參數中傳遞。不過,假如在搜索配置文件的android:searchSuggestSelection屬性中包含了選項值,那麼請求文本將作爲字符串數組selectionArgs的第一個元素傳遞。這兩種方式將在下文中一起討論。
讀取Uri中的搜索請求
默認情況下,請求文本是附加在uri參數(Uri對象)的末尾的。這時只要簡單地用getLastPathSegment()即可讀取請求文本了。例如:
String query = uri.getLastPathSegment().toLowerCase();
這將返回Uri的末尾部分(last segment),也就是用戶錄入的請求文本。
讀取selection參數中的搜索請求
與URI不同,還可以採用更智能的方式,query()方法能夠接收執行搜索所需的任何信息,selection和selectionArgs屬性也能夠傳遞合適的值。這種情況下,只要把SQLite查詢語句作爲android:searchSuggestSelection屬性加入到搜索配置文件中去即可。在查詢語句中,問號用作佔位符,代表實際的查詢請求。系統將用該查詢語句作爲selection參數,搜索請求作爲selectionArgs數組的第一個元素,對query()進行調用。
以下是如何定義android:searchSuggestSelection屬性來創建全文搜索語句的示例:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
</searchable>
通過以上配置,query()方法會把selection參數賦爲"word MATCH ?",selectionArgs參數賦爲搜索請求。當這些信息作爲參數傳給SQLite query()方法時,會被組合在一起(問號由搜索請求文本代替)。如果選擇以這種方式接收建議項請求並需要在請求文本中加入通配符,可以在selectionArgs參數的後面(或前面)加入即可,因爲該值將用引號包裹並整體插入到問號所在位置。
以上例子中新出現的屬性是android:searchSuggestIntentActio
提示:如果不需要在android:searchSuggestSelection屬性中定義查詢語句,但還要在selectionArgs參數中接收請求文本,則只要簡單地把android:searchSuggestSelection屬性賦成非空值即可。這將導致請求文本傳遞給selectionArgs,並且selection參數可被忽略。這樣就可以不用定義實際的底層查詢語句,content provider也不必再對其進行處理。
創建脫離數據庫表的Cursor
如果搜索建議項不是按照系統要求的數據列存儲在數據庫表中的(比如SQLite表),那可以在每次發起請求時檢索匹配的建議項數據並把它們格式化後保存到必要的表中。要實現這一目標,用系統要求的列名創建一個MatrixCursor,並用addRow(Object[])爲每個建議項創建一行數據。Content Provider的query()方法將返回最終結果。
用Cursor向系統返回建議項時,每一行數據的列格式都是系統規定的。因此,無論是要把建議項數據存儲在本地或Web服務器的SQLite數據庫中,還是要以本地或web的其它格式存儲,都必須把建議項格式化爲表的一行數據,並用Cursor來表示。系統可以識別多個列,但有兩列是必需的:
整數類型的行ID,唯一標識建議項。在ListView中顯示建議項時,系統會用到該值。
代表建議項的字符串。
以下各列是可選的(大部分都會在後續章節中討論):
字符串。如果Cursor包含該列,那麼所有的建議項都提供兩行模式。本列中的字符串將會顯示爲第二行,它的字體較小並顯示主建議項文本的下方。也可以爲null或空串,表示沒有第二行文本。
drawable資源、content或文件URI串。如果Cursor包含該列,那麼所有建議項都提供圖標加文字模式,圖標居左顯示。該項可以爲null或0,表示本行沒有圖標。
drawable資源、content或文件URI串。如果Cursor包含該列,那麼所有建議項都提供圖標加文本模式,圖標居右顯示。該項可以爲null或0,表示本行沒有圖標。
intent action字符串。如果給定行中存在本列並且有值,那麼此處定義的action將用於格式化建議項的intent。如果未提供本值,則會採用搜索配置文件中的android:searchSuggestIntentActio
數據URI字符串。如果給定行中存在本列並且有值,那麼此值在格式化建議項時將作爲intent的data部分來使用。如果未提供本值,則會採用搜索配置文件中的android:searchSuggestIntentData部分。如果前面兩處都未提供值,則intent的data部分將爲null。如果所有建議項的數據都是相同的,或者能用固定部分+ID來描述的,那麼更有效的的方式是用android:searchSuggestIntentData指定,並省略本列。
URI路徑字符串。如果給定行中存在本列並且有值,那麼“/”加上本值將會添加到intent的data之後。僅當搜索配置文件中的android:searchSuggestIntentData屬性已經設置了適當的基礎數據字符串時,本項纔會用到。
SUGGEST_COLUMN_INTENT_EXTRA_DATA
任何數據。如果給定行中存在本列並且有值,則表示格式化建議項intent時需要用到的extra data。如果未提供本值,則intent的extra data部分爲null。本列允許建議項附帶額外的數據,包含於intent的EXTRA_DATA_KEY鍵內。
如果給定行中存在本列並且有值,則爲格式化建議項請求時需要用到的數據,包含於intent的QUERY鍵內。當建議項的action是ACTION_SEARCH時需要用到本值,否則則是可選列。
僅當向“快速搜索框”提供建議項時纔會用到。本列標明瞭搜索建議項是否要存儲爲快捷方式,以及是否需要驗證有效性。快捷方式一般是在用戶從“快速搜索框”中點擊建議項時生成。如果本ID缺失的話,結果將會存儲爲快捷方式且不再會更新。如果設置爲SUGGEST_NEVER_MAKE_SHORTCUT,結果將不會存儲爲快捷方式。否則,快捷方式的ID將用於檢查並更新建議項,這時還會用到SUGGEST_URI_PATH_SHORTCUT.。
SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING
僅用於向“快速搜索框”提供建議項。本列指明當“快速搜索框”內建議項的快捷方式更新後,是否用進度滾輪(spinner)替代SUGGEST_COLUMN_ICON_2所設的圖標。
以上各列中的一部分將會在後續章節中繼續討論。
聲明建議項所需的Intent
當用戶在搜索對話框或widget下方的列表內選中某個建議項時,系統會向搜索activity發送一個自定義的Intent。intent必須定義好action和data部分。
聲明intent的action
自定義建議項最常用的intent action是ACTION_VIEW,適用於要啓動某些應用的場合,類似單詞的釋義、聯繫人信息、網頁等。當然,intent action也可以是其它任何action,甚至每個建議項的action都可以不同。
根據全部建議項是否共用同一種intent action,可以用以下兩種方式來定義action:
a.
例如:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
</searchable>
b.
在建議項表中添加SUGGEST_COLUMN_INTENT_ACTION列,並把每個建議項所用到的action放入其中(類似"android.Intent.action.VIEW")。
也可以混合使用以上兩種方式。比如,可以包含android:searchSuggestIntentActio
注意:如果搜索配置文件中未包含android:searchSuggestIntentActio
聲明intent的data
當用戶選中一個建議項時,搜索activity將會接收到附帶指定action(如上節所述)的intent,但該intent還必須同時攜帶data才行,這樣搜索activity才能識別出選中的建議項。有一點尤爲重要,data應該是能唯一標識每個建議項的信息,比如SQLite表中的行ID。在收到intent時,可以利用getData()或getDataString().來讀取附帶的data。
可以用以下兩種方式來定義intent中的data:
a.
在建議項表中包含SUGGEST_COLUMN_INTENT_DATA列,以便爲每個intent 都提供必要的data信息,然後把每行的唯一信息填入。該列中的值將會原封不動地附加到intent 中。可以利用getData()或getDataString()來讀取它們。
提示: 通常表的行ID最容易被用作Intent data,因爲它總是唯一的。最簡便的方式就是用SUGGEST_COLUMN_INTENT_DATA列名作爲行ID的別名。具體示例請參閱支持檢索的字典例程,其中SQLiteQueryBuilder創建了一個列名和別名的映射關係。
b.
在搜索配置文件的android:searchSuggestIntentData屬性中聲明全部建議項公共的URI部分。例如:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
</searchable>
然後在建議項表中的SUGGEST_COLUMN_INTENT_DATA_ID列中放入每個建議項的final path(唯一部分)。用戶選中建議項後,系統會把android:searchSuggestIntentData中指定的字符串加上斜槓 ("/"),再加入各自SUGGEST_COLUMN_INTENT_DATA_ID列中的值,組成一個完整的content URI。然後就可以用getData()讀取Uri了。
如果需要在intent中表示更多信息,可以添加另一列SUGGEST_COLUMN_INTENT_EXTRA_DATA來存放與建議項相關的附加數據。在此列中存放的數據將被置於intent附帶Bundle的EXTRA_DATA_KEY部分中。
處理Intent
現在已經能利用自定義intent提供自定義的搜索建議項,用戶選中某建議項時,需要用搜索activity來處理這些intent。在這之前,搜索activity已經對ACTION_SEARCH intent進行了處理。下面是如何在activity的onCreate()回調方法中處理intent的示例:
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
}
在上例中,intent action是ACTION_VIEW,並且通過組合android:searchSuggestIntentData字符串和SUGGEST_COLUMN_INTENT_DATA_ID列,數據已包含了指向建議項的完整URI。該URI傳給本地的showResult()方法,對URI指定的建議項在content provider中執行查詢。
注意:在Android manifest文件中,不需要對android:searchSuggestIntentActio
如果用戶使用方向控制設備(比如軌跡球或d-pad)來瀏覽建議項列表,則默認情況下搜索請求文本不會被更新。不過可以暫時用當前焦點所在的建議項來改寫用戶請求文本,讓它顯示在搜索文本框中。這樣就能讓用戶看到建議的請求文本(假如合適的話)並能在發送請求之前選中搜索框來編輯請求文本。
可以通過以下方式來改寫搜索請求文本:
a.
b.
c.
一旦把應用程序配置成提供自定義搜索建議項的話,讓它適用於全局訪問的“快速搜索框”是很容易的,只要修改一下搜索配置文件,讓其包含值爲"true"的android:includeInGlobalSearch屬性即可。
唯一要進行額外工作的情況,就是需要向content provider請求讀權限的時候。這種情況下,需要添加<path-permission>元素,賦予“快速搜索框”對content provider的讀權限。例如:
<provider android:name="MySuggestionProvider"
</provider>
在上例中,provider先規定了對content的讀和寫權限。<path-permission>元素修改了這個規定,當存在"android.permission.GLOBAL_SEARCH"權限時,允許對路徑前綴爲"/search_suggest_query"的內容進行讀取訪問。這樣就對“快速搜索框”賦予向content provider查詢建議項的權限。
如果content provider未明確指定讀權限,默認情況下“快速搜索框”是可以讀取其中內容的。
即使已經把應用程序配置爲向“快速搜索框”提供建議項,缺省情況下也不會真正生效。是否要在“快速搜索框”中包含應用程序所提供的建議項,是需要由用戶選擇的。爲了啓用應用程序提供的搜索建議項,必須打開“可搜索的項”(在“搜索”的“搜索設置”菜單中)並指定應用程序爲可搜索項。
每個可用於“快速搜索框”的應用程序都會在“可搜索的項”設置頁面中有一個條目。條目中包含了應用程序名稱和簡要描述信息,這些信息描述了應用程序能夠搜索並提交給“快速搜索框”作爲建議項的內容。通過在搜索配置文件中添加android:searchSettingsDescriptio
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
</searchable>
android:searchSettingsDescriptio
請記住,用戶必須訪問設置菜單併爲應用程序啓用搜索建議項,然後搜索建議項才能出現在“快速搜索框中”。因此,假如搜索是應用程序的重要特性之一,則可能需要考慮通過某種方式提醒用戶進行設置——可以在第一次啓動應用程序時提醒一下,告訴用戶如何啓用“快速搜索框”中的搜索建議項。
用戶在“快速搜索框”選中的建議項可以自動變爲快捷方式。這些快捷方式是系統從content provider中複製過來的建議項,這樣就能快速訪問建議項,而不必再次查詢content provider。
默認情況下,“快速搜索框”所讀取的全部建議項都能啓用快捷方式。如果建議項數據經過一段時間後發生了變化,可以發起刷新快捷方式的請求。舉例來說,如果建議項指向動態數據,比如聯繫人的存在狀態,那就應該在顯示給用戶的時候刷新建議項的快捷方式。只要在建議項表中包含SUGGEST_COLUMN_SHORTCUT_ID列即可實現這一目標。利用該列可以把每個建議項的快捷方式配置爲以下動作:
a.
在SUGGEST_COLUMN_SHORTCUT_ID列中指定一個值,每次顯示快捷方式時都會去查詢建議項的最新版本。刷新請求返回後,快捷方式將會與最新數據一起顯示出來,這時建議項已經用最新信息刷新。刷新請求利用SUGGEST_URI_PATH_SHORTCUT的URI路徑發送至content
provider,(而不是SUGGEST_URI_PATH_QUERY)。
返回的Cursor應該包含數據列與原來相同的一條建議項,或者爲空——表示快捷方式不可用了(這時,建議項將會消失,快捷方式也會被刪除)。
如果建議項指向數據的刷新可能需要耗費很長時間,比如基於網絡的的刷新,則可以在建議項表中添加SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING列並賦值爲“true”,這樣刷新時就會把“右手”光標顯示爲進度滾輪。除“true”以外的任何值都不會顯示進度滾輪。
b.
把SUGGEST_COLUMN_SHORTCUT_ID列賦值爲SUGGEST_NEVER_MAKE_SHORTCUT。這時,建議項就不會複製爲快捷方式了。僅當確實不想讓以前複製過的建議項顯示出來,纔會這麼做。(如果在該列中提供正常的值並重新調用的話,建議項快捷方式將在刷新請求返回後才顯示出來。)
c.
建議項不會變化且可以保存爲快捷方式,則保持SUGGEST_COLUMN_SHORTCUT_ID列爲空即可。
如果建議項全都不會發生變化,那就根本不需要SUGGEST_COLUMN_SHORTCUT_ID列。
注意:上述值只是被視作應用程序的需求而已,“快速搜索框”有權最終確定是否建立建議項的快捷方式,它並不保證指定的建議項快捷方式動作一定會實現。
關於“快速搜索框”建議項的級別
一旦應用程序的搜索建議項能夠用於“快速搜索框”,則“快速搜索框”將會對其評級,並確定如何根據請求把建議項展現給用戶。這可能會依賴於有多少其它應用程序也對該請求返回了結果,也可能會根據用戶選中哪個應用程序來返回結果的頻度。建議項的評級並沒有固定的規則,對於給定的搜索請求是否顯示應用程序的建議項也是不能保證的。可以大致這麼認爲,高質量的結果會增加建議項顯示在顯著位置的可能性,而低質的建議項則更可能會獲得較低的級別或者乾脆就不會顯示。
關於自定義搜索建議項的完整範例,請參閱支持檢索的字典例程。