请细读文章后半部分,结合实例,亲笔写的总结。
Android为程序的搜索功能提供了统一的搜索接口,searchdialog和search widget。
searchdialog只能为于activity窗口的上方,search widget可以位于任何位置。
searchdialog和searchwidget的其他属性如下:
A:声音搜索。
B:根据最近的搜索结果,给出搜索建议。
C:根据我们程序的实际搜索结果,给出搜索建议。
注1:search widget在Android 3.0或更高版本才可用
searchdialog和search widget的搜索功能特性都一样,但是他们还有微小区别:
A,search dialog是一个被系统控制的UI组件。但他被用户激活的时候,它总是出现在activity的上方。
B,Android系统负责处理search dialog上所有的事件,当用户提交了查询,系统会把这个查询请求传输到我们的searchable activity,让searchable activity在处理真正的查询。C,search widget是SearchView的一个实例,你可以把它放在你的布局的任何地方。
D,默认的,search widget和一个标准的EditText widget一样,不能做任何事情。但是你可以配置它,让android系统处理所有的按键事件,把查询请求传输给合适的activity,可以配置它让它像search dialog一样提供search suggestions。
E,search widget在 Android 3.0或更高版本才可用.search dialog没有此项限制
当用户在search dialog或search widget中执行一个搜索的时候,系统会创建一个Intent,并把查询关键字保存在里面,
然后启动我们在AndroidManifest.xml中声明好的searchable activity,并把Intent传送给它。
实现一个可以搜索的程序,主要需要以下几个部份:
(1)search dialog or widget的配置文件。
配置一个XML文件用于配置search dialog 或widget的设置。对于search dialog,该配置文件的名字一般约定为searchable.xml
(2)searchable activity。
searchable activity用于接收搜索关键字,并进行数据搜索和显示搜索结果。
(3)搜索条。search dialog 或search widget
* The search dialog:默认search dialog是隐藏。当我们按下了SEARCH键或在程序中调用onSearchRequested(),它将出现在屏幕的上方。
* SearchView widget:使用search widget的时候,你可以把该搜索框放在我们activity的任何地方,通常把它作为Action Bar的一个菜单而不是放到Layout的xml里面,对于用户来说会显得更加方便。
具体实现步骤(以SDK自带的Sample SearchableDictionary工程分析举例):
(1) 创建配置文件searchable.xml,存放于res/xml文件夹下
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
android:searchSettingsDescription="@string/settings_description"
//搜索功能描述 android:searchSuggestAuthority="com.example.android.searchabledict.DictionaryProvider" //Content Provider的主机名,它必须和你的content provider的mainfest中的 android:authorities一致
android:searchSuggestIntentAction="android.intent.action.VIEW"
//可选项,定义当用户选择了suggestion后发送给activity的Intent的Action,默认为Intent.ACTION_SEARCH
android:searchSuggestIntentData="content://com.example.android.searchabledict.DictionaryProvider/dictionary"
//定义当用户选择了suggestion后的发送给activity的intent的data,比如:
“content://com.example.android.searchabledict.DictionaryProvider/dictionary/1”(abbey这个单词的Uri)
android:searchSuggestSelection=" ?"
//它就作为selection参数传入到你的suggetion content provider的query函数中,固定为空格 +?
android:searchSuggestThreshold="1"
android:includeInGlobalSearch="true"> //是否支持全局搜索,就是从桌面搜索
</searchable>
关于searchable.xml更多内容请参考(官方英文文档):
http://developer.android.com/guide/topics/search/searchable-config.html
或(中文翻译):
http://hubingforever.blog.163.com/blog/static/171040579201142911524541/
(2) 创建SearchableActivity
searchable activity根据搜索关键字进行搜索,并显示搜索结果
1). MENIFEST.XML声明searchable activity
<activity
android:name=".SearchableDictionary"
android:launchMode="singleInstance" >
<intent-filter>
ion android:name="android.intent.action.MAIN"/>
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
ionandroid:name="android.intent.action.SEARCH" />
</intent-filter>
ta
android:name="android.app.searchable"
android:resource="@xml/searchable"/>
</activity>
<meta-data
android:name="android.app.default_searchable"
android:value=".SearchableDictionary" />
//可有可无,这个主要是想让app的任何地方都可以实施搜索并向该activity发送Intent
2) 创建Searchable Activity
SearchableDictionary.java:
它重写了onNewIntent方法来处理新的Intent,如果这个activity是由我们手动点击应用图标启动的,Intent的action为Intent.ACTION_MAIN,不会执行handleIntent里面的处理搜索的代码。另一方面它在菜单里又定义了一个搜索按钮作为actionbar的一部分显示在屏幕顶端了。当我们点击这个按钮时,通过直接调用onSearchRequested();就会在屏幕顶端出现一个在搜索框(Search Dialog),向里面输入英文单词(注意:会有suggestions下拉列表,后面再说它的配置),当你点击了其中一条或者按下了软键盘的右下角的“开始“键,就会立即给SearchableDictionary这个SearchableActivity发送一个Intent,其action根据你刚才的操作(是点了suggestion还是按了“开始”键)分别为:Intent.ACTION_VIEW和Intent.ACTION_SEARCH,其中这个IntentIntent.ACTION_VIEW是在searchable.xml里面自主定义的。接着触发onNewIntent方法。
若是单击suggestion来的,那么intent.getData()可以获取这个suggestion的唯一Uri(比如:
“content://com.example.android.searchabledict.DictionaryProvider/dictionary/1”),然后启动WordActivity查询数据库并显示word这个单词的释义;若是按了“开始”键来的,那么intent.getStringExtra(SearchManager.QUERY);可以获取此次要搜索的关键词,然后通过showResults方法搜索数据库并显示搜索结果,可能不止一条结果,所以开头定义了一个ListView。
(3)创建ContentProvider
? MENIFEST.XML里面声明:
<provider
android:name=".DictionaryProvider"
android:authorities="com.example.android.searchabledict.DictionaryProvider" />
? 实现ContentProvider实现Suggestions
DictionaryProvider.java:
里面主要是public Cursor query(…)这个方法,提供显示搜索建议的Cursor,之前里面定义了一个UriMatcher,用于匹配识别搜索类型,当URI匹配时返回相应的匹配码:就是指当搜索框自动访问ContentProvider获取搜索建议时,URI是
“content://com.example.android.searchabledict.DictionaryProvider/search_suggest_query?limit=50”,正好匹配AUTHORITY + SearchManager.SUGGEST_URI_PATH_SHORTCUT,返回匹配码SEARCH_SUGGEST,通过getSuggestions(selectionArgs[0])返回suggestions的cursor.当我们点击suggestions的任意一条后,search dialog消失并会向activity发送Intent,此时这个Intent包含了一条这条suggestion的uri,(比如“content://com.example.android.searchabledict.DictionaryProvider/dictionary/1”)那么activity可以通过这个uri访问ContentProvider来获取cursor并显示结果,因此当UriMatcher匹配到这个uri时返回匹配码GET_WORD,通过调用getWord(uri)获取cursor。
当我们没有点击所给的suggestion而是点击了”开始”键强行搜索时,uri会是
“content://com.example.android.searchabledict.DictionaryProvider/dictionary”,则返回匹配码SEARCH_WORDS,通过search(selectionArgs[0])搜索数据库并返回cursor。
匹配码REFRESH_SHORTCUT目前还用不到。
最后说明搜索框获得suggestions的cursor后,如何知道显示那些列的内容,
SearchManager 定义了两个固定列名:SearchManager.SUGGEST_COLUMN_TEXT_1和SearchManager.SUGGEST_COLUMN_TEXT_2,显示suggestions是就是取这两列的内容显示,矛盾的是我们的cursor对应的数据库表的列名几乎不可能都采用这两个固定的列名,解决办法是将查询到的结果表的列名重新命名为这两个固定列名。因此可以在DictionaryDatabase.java中看到定义了一个HashMap,用于映射原列名和改后的列名,这个HashMap应用于SQLiteQueryBuilder对象,就可以完成查询后结果表的列的重命名工作。
前面说过一个URI:
content://com.example.android.searchabledict.DictionaryProvider/dictionary/1,
他是怎么来的?由于前面一部分来自searchable.xml里面的定义android:searchSuggestIntentData,最后的1是id值,那么这个id值它是怎么获得的呢?你以为是取 _id这列的值吗,我们通常android里面数据库的主键通常用_id,但这不是必须的,要能获得id值,android在SearchManager又定义了一个列名:
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID用来获取id这个值,看它的名字就因该知道它是作为Intent的data的uri最后的id了。因此一般还需要在HashMap映射_id为SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID。
附:android系统内置的ContentResolver 查询数据库并重命名列的方法(关键在projection):
Cursor cursor = resolver.query(uri, new String[] {
MediaStore.Audio.Media._ID + " AS _id",
MediaStore.Audio.Media._ID+ " AS suggest_intent_data_id",
MediaStore.Audio.Media.TITLE+ " AS suggest_text_1",
MediaStore.Audio.Media.ARTIST+ " AS suggest_text_2" },
"title like'%" + keywd + "%' or artist like '%" + keywd
+"%' ",
null,
null);