Android?搜索框架使用詳解
搜索框架簡介
App中搜索功能是必不可少的,搜索功能可以幫助用戶快速獲取想要的信息。對此,Android提供了一個搜索框架,本文介紹如何通過搜索框架實現(xiàn)搜索功能。
Android 搜索框架提供了搜索彈窗和搜索控件兩種使用方式。
- 搜索彈窗:系統(tǒng)控制的彈窗,激活后顯示在頁面頂部,輸入的內(nèi)容提交后會通過
Intent傳遞到指定的搜索Activity中處理,可以添加搜索建議。 - 搜索控件(
SearchView):系統(tǒng)實現(xiàn)的搜索控件,可以放在任意位置(可以與Toolbar結(jié)合使用),默認情況下與EditText類似,需要自己添加監(jiān)聽處理用戶輸入的數(shù)據(jù),通過配置可以達到與搜索彈窗一致的行為。
使用搜索框架實現(xiàn)搜索功能
可搜索配置
在res/xml目錄下創(chuàng)建searchable.xml(必須用此命名),如下:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="@string/search_hint"
android:label="@string/app_name" />
android:label是此配置文件必須配置的屬性,通常配置為App的名字,android:hint配置用戶未輸入內(nèi)容時的提示文案,官方建議格式為“搜索${content or product}”。
更多可搜索配置包含的語法和用法可以看官方文檔。
搜索頁面
配置一個單獨的Activity用于顯示搜索內(nèi)容,用戶可能會在搜索完一個內(nèi)容后立刻搜索下一個內(nèi)容,所以建議把搜索頁面設(shè)置為SingleTop,避免重復(fù)創(chuàng)建搜索頁面。
在AndroidManifest中配置搜索頁面,如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
...
>
<activity
android:name=".search.SearchActivity"
android:exported="false"
android:launchMode="singleTop">
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
</activity>
</application>
</manifest>
在Activity中處理搜索數(shù)據(jù),代碼如下:
class SearchActivity : AppCompatActivity() {
private lateinit var binding: LayoutSearchActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.layout_search_activity)
// 當搜索頁面第一次打開時,獲取搜索內(nèi)容
getQueryKey(intent)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// 更新Intent數(shù)據(jù)
setIntent(intent)
// 當搜索頁面多次打開,并仍在棧頂時,獲取搜索內(nèi)容
getQueryKey(intent)
}
private fun getQueryKey(intent: Intent?) {
intent?.run {
if (Intent.ACTION_SEARCH == action) {
// 用戶輸入的內(nèi)容
val queryKey = getStringExtra(SearchManager.QUERY) ?: ""
if (queryKey.isNotEmpty()) {
doSearch(queryKey)
}
}
}
}
private fun doSearch(queryKey: String) {
// 根據(jù)用戶輸入內(nèi)容執(zhí)行搜索操作
}
}
使用SearchView
SearchView可以放在頁面的任意位置,本文與Toolbar結(jié)合使用,如何在Toolbar中創(chuàng)建菜單項在上一篇文章中介紹過,此處省略。要使SearchView與搜索彈窗保持一致的行為需要在代碼中進行配置,如下:
class SearchActivity : AppCompatActivity() {
private lateinit var binding: LayoutSearchActivityBinding
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.example_seach_menu, menu)
menu?.run {
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
val searchView = findItem(R.id.action_search).actionView as SearchView
//設(shè)置搜索配置
searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName))
}
return true
}
...
}
使用搜索彈窗
在Activity中使用搜索彈窗,如果Activity已經(jīng)配置為搜索頁面則無需額外配置,否則需要在AndroidManifest中添加配置,如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
...
>
<activity
android:name=".search.SearchExampleActivity">
<!--為當前頁面指定搜索頁面-->
<!--如果所有頁面都使用搜索彈窗,則將此meta-data移到applicaion標簽下-->
<meta-data
android:name="android.app.default_searchable"
android:value=".search.SearchActivity" />
</activity>
</application>
</manifest>
在Activity中通過onSearchRequested方法來調(diào)用搜索彈窗,如下:
class SearchExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: LayoutSearchExampleActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_search_example_activity)
binding.btnSearchDialog.setOnClickListener { onSearchRequested() }
}
}
搜索彈窗對Activity生命周期的影響
搜索彈窗的顯示隱藏,不會像其他彈窗一樣觸發(fā)Activity的onPause、onResume方法。如果在搜索彈窗顯示隱藏的同時需要對其他功能進行處理,可以通過onSearchRequested和OnDismissListener來實現(xiàn),代碼如下:
class SearchExampleActivity : AppCompatActivity() {
override fun onSearchRequested(): Boolean {
// 搜索彈窗顯示,可以在此處理其他功能
return super.onSearchRequested()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: LayoutSearchExampleActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_search_example_activity)
binding.btnSearchDialog.setOnClickListener { onSearchRequested() }
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
searchManager.setOnDismissListener {
// 搜索彈窗隱藏,可以在此處理其他功能
}
}
}
附加額外的參數(shù)
使用搜索彈窗時,如果需要附加額外的參數(shù)用于優(yōu)化搜索查詢的過程,例如用戶的性別、年齡等,可以通過如下代碼實現(xiàn):
// 配置額外參數(shù)
class SearchExampleActivity : AppCompatActivity() {
override fun onSearchRequested(): Boolean {
val appData = Bundle()
appData.putString("gender", "male")
appData.putInt("age", 24)
startSearch(null, false, appData, false)
// 返回true表示已經(jīng)發(fā)起了查詢
return true
}
...
}
// 在搜素頁面中獲取額外參數(shù)
class SearchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
intent?.run {
if (Intent.ACTION_SEARCH == action) {
// 用戶輸入的內(nèi)容
val queryKey = getStringExtra(SearchManager.QUERY) ?: ""
// 額外參數(shù)
val appData = getBundleExtra(SearchManager.APP_DATA)
appData?.run {
val gender = getString("gender") ?: ""
val age = getInt("age")
}
}
}
}
}
語音搜索
語音搜索讓用戶無需輸入內(nèi)容就可進行搜索,要開啟語音搜索,需要在searchable.xml增加配置,如下:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="@string/search_hint"
android:label="@string/app_name"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer" />
語音搜索必須配置showVoiceSearchButton用于顯示語音搜索按鈕,配置launchRecognizer指定語音搜索按鈕啟動一個語音識別程序用于識別語音轉(zhuǎn)錄為文本并發(fā)送至搜索頁面。
更多語音搜索配置包含的語法和用法可以看官方文檔。
注意,語音識別后的文本會直接發(fā)送至搜索頁面,無法更改,需要進行完備的測試確保語音搜索功能適合你的App。
搜索記錄
用戶執(zhí)行過搜索后,可以將搜索的內(nèi)容保存下來,下次要搜索相同的內(nèi)容時,輸入部分文字后就會顯示匹配的搜索記錄。
要實現(xiàn)此功能,需要完成下列步驟:
創(chuàng)建SearchRecentSuggestionsProvider
自定義RecentSearchProvider繼承SearchRecentSuggestionsProvider,代碼如下:
class RecentSearchProvider : SearchRecentSuggestionsProvider() {
companion object {
// 授權(quán)方的名稱(建議設(shè)置為文件提供者的完整名稱)
const val AUTHORITY = "com.chenyihong.exampledemo.search.RecentSearchProvider"
// 數(shù)據(jù)庫模式
// 必須配置 DATABASE_MODE_QUERIES
// 可選配置 DATABASE_MODE_2LINES,為搜索記錄提供第二行文本,可用于作為詳情補充
const val MODE: Int = DATABASE_MODE_QUERIES or DATABASE_MODE_2LINES
}
init {
// 設(shè)置搜索授權(quán)方的名稱與數(shù)據(jù)庫模式
setupSuggestions(AUTHORITY, MODE)
}
}
在AndroidManifest中配置Provider,如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
...
>
<!--android:authorities的值與RecentSearchProvider中的AUTHORITY一致-->
<provider
android:name=".search.RecentSearchProvider"
android:authorities="com.chenyihong.exampledemo.search.RecentSearchProvider"
android:exported="false" />
</application>
</manifest>
修改可搜索配置
在searchable.xml增加配置,如下:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="@string/search_hint"
android:label="@string/app_name"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
android:searchSuggestAuthority="com.chenyihong.exampledemo.search.RecentSearchProvider"
android:searchSuggestSelection=" ?"/>
android:searchSuggestAuthority 的值與RecentSearchProvider中的AUTHORITY保持一致。android:searchSuggestSelection的值必須為" ?",該值為數(shù)據(jù)庫選擇參數(shù)的占位符,自動由用戶輸入的內(nèi)容替換。
在搜索頁面中保存查詢
獲取到用戶輸入的數(shù)據(jù)時保存,代碼如下:
class SearchActivity : BaseGestureDetectorActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
intent?.run {
if (Intent.ACTION_SEARCH == action) {
val queryKey = getStringExtra(SearchManager.QUERY) ?: ""
if (queryKey.isNotEmpty()) {
// 第一個參數(shù)為用戶輸入的內(nèi)容
// 第二個參數(shù)為第二行文本,可為null,僅當RecentSearchProvider.MODE為DATABASE_MODE_QUERIES or DATABASE_MODE_2LINES時有效。
SearchRecentSuggestions(this@SearchActivity, RecentSearchProvider.AUTHORITY, RecentSearchProvider.MODE)
.saveRecentQuery(queryKey, "history $queryKey")
}
}
}
}
}
清除搜索歷史
為了保護用戶的隱私,官方的建議是App必須提供清除搜索記錄的功能。請求搜索記錄可以通過如下代碼實現(xiàn):
class SearchActivity : BaseGestureDetectorActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
SearchRecentSuggestions(this, RecentSearchProvider.AUTHORITY, RecentSearchProvider.MODE)
.clearHistory()
}
}
示例
整合之后做了個示例Demo,代碼如下:
// 可搜索配置
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="@string/search_hint"
android:label="@string/app_name"
android:searchSuggestAuthority="com.chenyihong.exampledemo.search.RecentSearchProvider"
android:searchSuggestSelection=" ?"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer" />
// 清單文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
...
>
<activity
android:name=".search.SearchExampleActivity"
android:screenOrientation="portrait">
<!--為當前頁面指定搜索頁面-->
<meta-data
android:name="android.app.default_searchable"
android:value=".search.SearchActivity" />
</activity>
<activity
android:name=".search.SearchActivity"
android:exported="false"
android:launchMode="singleTop"
android:parentActivityName="com.chenyihong.exampledemo.search.SearchExampleActivity"
android:screenOrientation="portrait">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.chenyihong.exampledemo.search.SearchExampleActivity" />
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
</activity>
<provider
android:name=".search.RecentSearchProvider"
android:authorities="com.chenyihong.exampledemo.search.RecentSearchProvider"
android:exported="false" />
</application>
</manifest>
// 示例Activity
class SearchExampleActivity : BaseGestureDetectorActivity() {
override fun onSearchRequested(): Boolean {
val appData = Bundle()
appData.putString("gender", "male")
appData.putInt("age", 24)
startSearch(null, false, appData, false)
return true
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: LayoutSearchExampleActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_search_example_activity)
binding.btnSearchView.setOnClickListener { startActivity(Intent(this, SearchActivity::class.java)) }
binding.btnSearchDialog.setOnClickListener { onSearchRequested() }
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
searchManager.setOnDismissListener {
runOnUiThread { Toast.makeText(this, "Search Dialog dismiss", Toast.LENGTH_SHORT).show() }
}
}
}
class SearchActivity : BaseGestureDetectorActivity() {
private lateinit var binding: LayoutSearchActivityBinding
private val textDataAdapter = TextDataAdapter()
private val originData = ArrayList<String>()
private var lastQueryValue = ""
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.example_seach_menu, menu)
menu?.run {
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
val searchView = findItem(R.id.action_search).actionView as SearchView
searchView.setOnCloseListener {
textDataAdapter.setNewData(originData)
false
}
searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName))
if (lastQueryValue.isNotEmpty()) {
searchView.setQuery(lastQueryValue, false)
}
}
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.action_clear_search_histor) {
SearchRecentSuggestions(this, RecentSearchProvider.AUTHORITY, RecentSearchProvider.MODE)
.clearHistory()
}
return super.onOptionsItemSelected(item)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.layout_search_activity)
setSupportActionBar(binding.toolbar)
supportActionBar?.run {
title = "SearchExample"
setHomeAsUpIndicator(R.drawable.icon_back)
setDisplayHomeAsUpEnabled(true)
}
binding.rvContent.adapter = textDataAdapter
originData.add("test data qwertyuiop")
originData.add("test data asdfghjkl")
originData.add("test data zxcvbnm")
originData.add("test data 123456789")
originData.add("test data /.,?-+")
textDataAdapter.setNewData(originData)
// 獲取搜索內(nèi)容
getQueryKey(intent, false)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// 更新Intent數(shù)據(jù)
setIntent(intent)
// 獲取搜索內(nèi)容
getQueryKey(intent, true)
}
private fun getQueryKey(intent: Intent?, newIntent: Boolean) {
intent?.run {
if (Intent.ACTION_SEARCH == action) {
val queryKey = getStringExtra(SearchManager.QUERY) ?: ""
if (queryKey.isNotEmpty()) {
SearchRecentSuggestions(this@SearchActivity, RecentSearchProvider.AUTHORITY, RecentSearchProvider.MODE)
.saveRecentQuery(queryKey, "history $queryKey")
if (!newIntent) {
lastQueryValue = queryKey
}
val appData = getBundleExtra(SearchManager.APP_DATA)
doSearch(queryKey, appData)
}
}
}
}
private fun doSearch(queryKey: String, appData: Bundle?) {
appData?.run {
val gender = getString("gender") ?: ""
val age = getInt("age")
}
val filterData = originData.filter { it.contains(queryKey) } as ArrayList<String>
textDataAdapter.setNewData(filterData)
}
}
效果如圖:

以上就是Android 搜索框架使用詳解的詳細內(nèi)容,更多關(guān)于Android 搜索框架使用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android使用Messenger實現(xiàn)service與activity交互
這篇文章主要介紹了android使用Messenger實現(xiàn)service與activity交互的相關(guān)資料,需要的朋友可以參考下2016-06-06
Android 對手機網(wǎng)絡(luò)的檢測和監(jiān)聽的方法示例
本篇文章主要介紹了Android 對手機網(wǎng)絡(luò)的檢測和監(jiān)聽的方法示例,主要使用BroadcastReceiver廣播接收器來接收網(wǎng)絡(luò)狀態(tài),現(xiàn)在分享給大家,也給大家做個參考,有興趣的一起來了解一下2018-03-03
詳解Xamarin.Android 利用Fragment實現(xiàn)底部菜單
這篇文章主要介紹了詳解Xamarin.Android 利用Fragment實現(xiàn)底部菜單,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02
Android IPC機制ACtivity綁定Service通信代碼實例
這篇文章主要介紹了Android IPC機制ACtivity綁定Service通信代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-09-09
Android App內(nèi)監(jiān)聽截圖加二維碼功能代碼
Android截屏功能是一個常用的功能,可以方便的用來分享或者發(fā)送給好友,本文介紹了如何實現(xiàn)app內(nèi)截屏監(jiān)控功能,需要的朋友可以參考下2017-08-08
Android基于Xposed修改微信運動步數(shù)實例
這篇文章主要介紹了Android基于Xposed修改微信運動步數(shù)實例,需要的朋友可以參考下2017-06-06
Android開發(fā)之利用Activity實現(xiàn)Dialog對話框
這篇文章主要給大家介紹了Android開發(fā)之如何利用Activity實現(xiàn)Dialog對話框效果,文中給出了詳細的示例代碼,相信對大家的理解及學習具有一定的參考借鑒價值,有需要的朋友們下面來一起看看吧。2016-12-12
解析Android中string-array數(shù)據(jù)源的簡單使用
本篇文章是對Android中string-array數(shù)據(jù)源的使用進行了詳細的分析介紹,需要的朋友參考下2013-06-06
內(nèi)存泄露導(dǎo)致Android?中setVisibility()?失效原理
這篇文章主要介紹了內(nèi)存泄露導(dǎo)致Android?中setVisibility()?失效原理,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-07-07

