Creating a Search Interface
版本:Android 4.0 r1
快速查看
- Android系统把搜索请求从搜索对话框或widget发送给执行搜索并显示结果的activity
- 可以把搜索widget作为“action view”放入Action Bar中,用于快速访问
在本文中
关键类
相关示例
Action Bar中的SearchView
下载
参阅
如果要在应用程序中添加搜索功能,Android将会帮助程序实现用户界面,可以用显示在activity窗口顶端的搜索对话框, 也可以选用能够置入程序layout的搜索widget,两者都可以把用户的搜索请求传递给程序指定的activity。 这样,用户就能从任何一个支持搜索框或搜索widget的activity中发起搜索,系统会启动合适的activity来执行搜索并显示结果。
搜索对话框和widget支持的其它特性包括:
- 语音搜索
- 根据最近的请求给出搜索建议项
- 匹配应用系统实际数据的搜索建议项
本指南展示了应用程序如何利用搜索对话框或者搜索widget来提供搜索界面,并在Android系统支持下从该界面中发送搜索请求。
首先,应该确定是否要用搜索对话框或搜索widget来实现搜索界面。这两者提供的搜索功能是一样的,只是在以下方面有细微差别:
- 搜索对话框是一个由Android 系统控制的UI组件。当被用户激活时,搜索对话框显示在activity的顶端,如图1所示。
图1.某应用程序搜索对话框的屏幕截图
Android系统控制搜索对话框中所有的事件。当用户提交一个请求时,系统将把该请求传递给预设的处理搜索的activity。搜索对话框还能在用户键入时提供搜索建议项。 - 搜索widget是一个 SearchView的实例,可以把它放入layout的任何位置。默认情况下,搜索widget的显示方式类似于一个标准的 EditText widget,并且不会执行任何动作。但可以对其进行配置,让Android系统来处理所有录入事件、把搜索请求传递给相应的activity、提供搜索建议项(如同搜索对话框一样)。 不过,只有Android 3.0 (API Level 11) 以上版本才支持搜索widget。
注意:如果需要的话,可以利用多种回调方法和侦听器(listener),来自行处理搜索widget内的所有用户输入。 不过本文重点关注如何结合系统和搜索widget来实现搜索功能。如果要自行处理所有的用户输入,请阅读 SearchView及其内部接口的参考文档。
一旦用户在搜索对话框或搜索widget中执行了搜索,系统就会创建一个 Intent并把用户的请求置入其中。然后系统会启动activity并把intent传给它,该activity是已声明用于处理搜索的activity(“searchable activity”)。如果应用程序要设置为这种搜索方式,则需要:
- 搜索配置文件
一个XML文件,定义了一些有关搜索对话框或widget的配置信息。包括了诸如语音搜索、搜索建议项和搜索框的提示文本等特性的设置。 - 搜索activity
接收搜索请求的 Activity ,用于搜索数据并显示搜索结果。 - 搜索界面,可由以下方式之一提供:
- 搜索对话框
默认情况下,搜索对话框是隐藏的。当用户按下设备上的“搜索”键(如果有的话)或者界面上的某个按钮时,搜索框将出现在屏幕的顶端。 - 或者, SearchViewwidget
使用搜索widget可以把搜索框放入应用程序activity的任何位置。不过对于用户而言,通常将它用作Action Bar中的action显得更为便捷,而不是放入某个activity layout中。
- 搜索对话框
本文以下部分展示了如何创建搜索配置文件、搜索activity,并用搜索对话框和搜索widget实现了一个搜索界面。
首先需要创建一个称为搜索配置文件(searchable configuration)的XML文件,它设置了搜索对话框或widget的用户界面外观,也定义了诸如搜索建议项、语音搜索之类的功能特性。该文件习惯上命名为 searchable.xml,且必须存放于res/xml/ 目录下。
注意:系统将利用该文件来初始化 SearchableInfo 对象,但在运行时无法自行创建此对象——只能在XML文件中声明搜索配置。
搜索配置文件必须包含 <searchable> 元素作为根节点,并指定一个以上的属性。例如:
<?xml version="1.0" encoding="utf-8"?>
<searchablexmlns:android="http://schemas.android.com/apk/res/android"
</searchable>
只有android:label是必需的属性,它指向字符串资源且应该是应用程序的名称。用户只有在“快速搜索框”中启用搜索建议项时才会看到这个文本标签。这种情况下,该标签会显示在系统设置的“可搜索项”列表中。
虽然android:hint属性不是必需项,但还是建议都要包含它。用户录入搜索文本前,它能在搜索框中显示提示信息。这个提示信息相当重要,因为它为用户能够搜到什么提供了重要线索。
提示:为了与其它Android应用程序保持一致,应该把android:hint设置为“搜索 <内容或产品>”的格式。例如:“搜索歌名或歌手名”或“搜索YouTube”。
<searchable> 元素还可以包含很多其它参数。不过,在加入诸如搜索建议项和语音搜索特性之前,大部分属性都不需要用到。关于搜索配置文件的详细信息,请参阅搜索配置文件 参考文档。
创建搜索Activity
搜索activity是指应用程序中根据请求文本执行搜索并显示结果的Activity。
当用户在搜索对话框或widget中执行搜索时,系统将启动搜索activity,并把搜索请求置入附带ACTION_SEARCHaction的Intent中传递给它。搜索activity从intent附带的QUERY中读取查询请求,然后搜索数据并显示结果。
因为搜索对话框或widget可能是位于应用程序的其它activity中,所以系统必须知道哪个activity将作为搜索activity使用,以便正确地传送搜索请求。因此,必须首先在Android manifest文件中声明搜索activity。
声明搜索activity
如果没有现成的activity,就创建一个执行搜索并显示结果的Activity。现在还不需要实现搜索功能——只是在manifest声明一下来创建activity。在manifest的<activity>元素中:
1.
2.
例如:
<application ... >
</application>
<meta-data>元素必须包含值为"android.app.searchable"的android:name属性,以及指向搜索配置文件的android:resource属性(此例中,指向res/xml/searchable.xml文件)。
注意:<intent-filter> 不需要指定值为DEFAULT的<category>属性(通常<activity> 元素中是会见到的),因为系统会利用组件名称把ACTION_SEARCH intent明确地传递给搜索activity。
一旦在manifest中完成了搜索activity的声明,在搜索activity中执行搜索就涉及到以下三步:
1.
2.
3.
按照惯例,搜索结果应该显示在ListView中,因此搜索activity可能需要继承自ListActivity。包括默认带一个ListView的layout,以及很多便于使用ListView的方法。
当用户从搜索对话框或widget中执行搜索时,系统将会启动搜索activity,并向它发送一个ACTION_SEARCHintent。该intent在QUERY字符串中附带了搜索请求。搜索activity启动后必须检查此intent并解析该字符串。例如:以下是搜索activity启动时如何读取查询请求的示例:
@Override
publicvoid onCreate(Bundle savedInstanceState){
}
ACTION_SEARCH intent总是包含QUERY字符串的。此例中,查询请求读取后又传给了doMySearch()方法,实际的搜索操作在该方法中执行。
存储和检索数据的过程对于应用程序而言应该是唯一的。可以采用多种方式存储和搜索数据,而本文并不展示如何存储和检索数据。存储和检索数据应该是依据需求和数据格式来仔细考虑的问题。下面是一些可能有用的提示:
·
·
关于适配器Adapter
适配器Adapter用于把一组数据绑定到View对象中。当把Adapter提交给ListView时,每项数据将作为单独的view插入到列表中。Adapter仅仅是一个接口,因此诸如CursorAdapter(绑定Cursor的数据)还是需要去实现代码的。如果还没有实现对数据的处理,则可以自己从BaseAdapter实现一个。安装API Level 4的SDK示例包,可以看到“支持检索的字典”的原始版本,它创建了一个自定义的适配器来读取文件数据。
不论数据是否可用,也不管搜索方式如何,都建议用Adapter把搜索结果返回给搜索activity。这样就可以很容易地把所有的搜索结果显示在ListView中。如果数据来自SQLite数据库查询,可以用CursorAdapter把结果提交给ListView。如果数据来源是其它类型的,则可以创建一个继承自BaseAdapter的类。
如上所述,建议把ListView作为显示搜索结果的用户界面,这样可能就需要让搜索activity继承自ListActivity。可以调用setListAdapter(),并把绑定了数据的Adapter传递给它。这就把所有搜索结果装入了activity的ListView中。
更多关于显示结果列表的有用信息,请参阅ListActivity文档。
关于如何检索SQLite数据库以及用Adapter提交结果给ListView的完整示例,请参阅支持检索的字典例程。
应该用搜索对话框还是widget?
答案大致取决于是否为Android 3.0 (API Level 11) 以上版本进行开发,因为SearchView widget自Android 3.0才开始引入。因此,如果是为低于3.0版的Android开发程序,就不能选择搜索widget,应使用搜索对话框来实现搜索界面。
如果正在为Android 3.0以上版本进行开发,则答案更多取决于需求。大多数情况下,建议把搜索widget作为Action Bar 中的一个“action view”来使用。不过,因为某些原因(也许没有足够的空间或者不会用到Action Bar),可能无法把搜索widget放入Action Bar。这样就可能需要把搜索widget放入自己的activity layout中去。假如没有其它方式可用且可以忍受不可见的搜索框,则还是可以换用搜索对话框。实际上在某些场合,可能需要同时提供对话框和widget。关于widget的详情,请跳转到使用搜索Widget.。
搜索对话框提供了位于屏幕顶部的浮动搜索框,搜索框左边带有应用程序图标。用户执行搜索时,搜索对话框可以在用户键入时提供搜索建议项,系统将把搜索请求发送给执行检索的搜索activity。不过,如果是为运行Android 3.0的设备开发应用的话,则应考虑改用搜索widget(参见上文)。
搜索对话框默认是隐藏的,除非用户激活它。如果用户设备带有“搜索”按钮,则默认情况下按下此按钮会激活搜索对话框。应用程序也可以在需要时调用onSearchRequested()激活搜索对话框。不过,这都要在activity中启用搜索对话框之后才能生效。
要启用搜索对话框,必须告诉系统哪个搜索activity将会从搜索对话框接收搜索请求并执行搜索。例如在上一节创建搜索Activity中,创建了一个名为SearchableActivity的搜索activity。如果需要一个单独的名为OtherActivity的activity,用于显示搜索对话框并发送搜索请求给SearchableActivity,则必须在manifest中声明:SearchableActivity是供OtherActivity中的搜索对话框使用的搜索activity。
要为activity中的搜索对话框声明搜索activity,请在activity各自的<activity>元素内加入<meta-data>元素。<meta-data>元素必须包含android:value属性,用于指定搜索activity类的名称,以及值为"android.app.default_searchable".的android:name属性。
下面是演示了搜索activity SearchableActivity和OtherActivity的例程,搜索由OtherActivity中的对话框发起,而SearchableActivity执行搜索:
<application ... >
</application>
因为OtherActivity现在包含了<meta-data>元素,即指明了使用哪个搜索activity来进行搜索,所以就表示OtherActivity已经启用了搜索对话框。当用户使用此activity时,设备的“搜索”按钮(如果有的话)和onSearchRequested()方法将会激活搜索对话框。用户一旦提交了搜索,系统将启动SearchableActivity并向其发送ACTION_SEARCH intent。
注意:搜索activity本身默认是支持搜索对话框的,因此不需要在SearchableActivity加入以上声明。
如果应用程序中所有的activity都需要支持搜索对话框,只要把上述<meta-data> 元素作为子节点插入<application>元素中即可,而不需要对每个<activity>进行声明。这样每个activity都会继承该值,对搜索对话框提供支持,并会把请求发送到同一个搜索activity中去。(如果存在多个搜索activity,可以把不同的<meta-data>声明放入各自的activity中,覆盖默认的搜索activity声明即可。)
activity开启搜索对话框后,应用程序就准备好执行搜索了。
如上所述,只要在manifest中对当前activity用到的搜索activity进行了声明,设备上的“搜索”按钮就会启动搜索对话框。
不过有些设备不提供专用的“搜索”按钮,所以不能假定该按钮总是可用的。如果要使用搜索对话框,必须保证在用户界面上另外提供一个搜索按钮,用于调用onSearchRequested()激活搜索对话框。
举例来说,应该在选项菜单提供一个菜单项,或者在activity layout上提供一个按钮,它们可以调用onSearchRequested()来激活搜索。search_icons.zip文件包含了适应中密度和高密度屏幕的图标,可用于搜索菜单项或按钮(在低密度屏幕上会把高分辨率hdpi图片缩小一半显示)。
还可以启用“键入搜索”(“type-to-search”)功能,使得用户开始键盘输入时激活搜索对话框——敲入的字符会插入到搜索对话框中去。通过在onCreate()方法内调用setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL),可以启用activity的“键入搜索”功能。
搜索对话框对activity生命周期的影响
搜索对话框是一个浮动在屏幕顶端的Dialog。它不会导致activity栈发生任何变化,因此搜索对话框出现时,不会调用任何生命周期方法(比如onPause())。activity只是失去焦点,因为输入焦点切换给了搜索对话框。
如果期望搜索对话框激活时能够获得通知,请覆盖onSearchRequested()方法。如果系统调用了该方法,就表明activity已经失去了焦点,而搜索对话框获取了焦点,这样就可以执行合适的生命周期事件操作了(比如暂停游戏)。除非正在传送搜索内容(在下文讨论),否则就应该调用父类来结束本方法。例如:
@Override
publicboolean onSearchRequested(){
}
如果用户按下“回退”按钮取消了搜索,则搜索对话框会关闭,activity将重新获得输入焦点。可以用setOnDismissListener()和/或setOnCancelListener()进行注册,使得搜索对话框关闭时能获得通知。应该只需要注册OnDismissListener即可,因为每次关闭搜索对话框时都会调用它。而OnCancelListener只有在用户显式退出搜索对话框时才会被调用,因此搜索执行完毕就不会调用它(这时搜索对话框自然会消失)。
如果当前activity不是搜索activity,则用户执行搜索后会触发正常的生命周期事件(当前activity会接收到onPause()等事件,如Activities文档中所述)。不过,如果当前activity是搜索activity,则会发生以下两件情况之一:
a.
b.
@Override
publicvoid onCreate(Bundle savedInstanceState){
}
@Override
protectedvoid onNewIntent(Intent intent){
}
privatevoid handleIntent(Intent intent){
}
对比执行搜索一节的示例代码可以发现,现在所有处理搜索intent的代码都放在handleIntent()方法中,这样onCreate()和onNewIntent()都能调用它。
系统调用onNewIntent(Intent)时,activity并未被重启,因此getIntent()方法返回的与onCreate()接收到的是同一个intent。这就是为什么应在onNewIntent(Intent)内调用setIntent(Intent)的原因(这样以后调用getIntent()时,activity保存的intent将会更新)。
上述使用“singleTop”启动模式的第二种情况通常是比较理想的,因为一旦搜索完成,用户就很有可能会执行其它搜索。如果应用程序创建了多个搜索activity实例,用户体验就会很糟糕。所以,建议在应用程序的manifest中把搜索activity设置为“singleTop”启动模式:
<activityandroid:name=".SearchableActivity"
某些情况下,在每次执行搜索的时候可能需在搜索activity中对搜索请求进行一些必要的优化。不过,如果是要根据用户发起搜索的activity对搜索标准进行修改,则可以在系统发给搜索activityde intent中附带额外数据。可以在APP_DATABundle中发送附加数据,它包含在ACTION_SEARCH intent中。
要发送这种数据给搜索activity,请覆盖搜索发起activity的onSearchRequested()方法,创建一个带有附加数据的Bundle,再调用startSearch()来激活搜索对话框。例如:
@Override
publicboolean onSearchRequested(){
返回“true”则表明已经成功处理了回调事件,调用startSearch()来激活搜索对话框。一旦用户提交了请求,附加数据将会一起传递给搜索activity。可以从APP_DATABundle中解析出附加数据并对请求进行优化。比如:
Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA);
if(appData !=null){
}
警告:永远不要在onSearchRequested()回调方法以外调用startSearch()。要在activity中激活搜索对话框,一定要调用onSearchRequested()。否则,onSearchRequested()将不会被调用,自定义的功能(比如上述附加appData数据的例子)将会失效。
使用搜索Widget
Android 3.0以上版本才支持SearchView widget。如果正在为Android 3.0开发应用程序,并决定使用搜索widget,则建议把搜索widget作为一个Action Bar中的action view 进行插入,从而代替搜索对话框(也代替把搜索widget放入activity layout的方式)。图2展示了Action Bar中的搜索widget 。
图2.SearchView widget用作Action Bar 中的“action view”
搜索widget提供的功能与搜索对话框一样。用户执行搜索时,它将启动相应的activity,并能支持搜索建议项和语音搜索。
注意:在把搜索widget作为action view使用时,可能仍需同时提供搜索对话框的支持,因为这种情况下搜索widget有可能不能适用于Action Bar。请参阅下节同时使用widget和对话框。
配置搜索widget
如上所述,在创建完搜索配置文件和搜索activity,需要启用SearchView中的相应搜索功能。可以通过调用setSearchableInfo()实现这一目的,请将表示搜索配置的SearchableInfo对象传入其中。
调用SearchManager中getSearchableInfo()的,可以获得一个SearchableInfo引用。
比如,假设正在把SearchView用作Action Bar中的action view,应该在onCreateOptionsMenu()回调方法中启用widget:
@Override
publicboolean onCreateOptionsMenu(Menu menu){
}
这就够了。搜索widget现在已配置完成,系统将会把搜索请求发送给搜索activity。也可以为搜索widget启用搜索建议项。
注意:如果要自行处理所有的用户输入,利用一些回调方法和事件侦听器即可。详细信息请参阅SearchView的参考文档,有关事件侦听器可参阅其内部的接口。
关于Action Bar中action views的详情,请参阅Action Bar开发者指南(内含把搜索widget作为action view加入其中的示例代码)。
其它搜索widget特性
SearchView widget还支持一些其它可能有用的特性:
提交按钮
默认情况下是没有用于提交查询请求的按钮的,用户必须按下键盘上的“回车”键来启动搜索。可以通过调用setSubmitButtonEnabled(true)来增加“提交”按钮。
精确确认搜索建议项中的搜索请求
如果启用了搜索建议项,通常是期望用户能简单地选中建议项即可,但是有可能需要对建议的搜索请求进行精确确认。通过调用setQueryRefinementEnable
切换搜索框的可见性
默认情况下,搜索widget是“图标化的”,也就是只用一个搜索图标(放大镜)来表示,当用户触摸时才会展开显示搜索框。如上所示,通过调用setIconifiedByDefault(false),也可以默认就把搜索框显示出来。还可以通过调用setIconified(),来切换搜索widget的显示方式。
在SearchView类中还有很多其它的API,可用于定制搜索widget。不过大部分API都只是在自行处理用户输入时才会用到,用于取代Android系统来发送搜索请求并显示搜索建议项。
同时使用widget和对话框
如果把搜索widget作为action view插入Action Bar,并且设置为Action Bar“空间够用时”才显示(设置android:showAsAction="ifRoom"),那么搜索widget有可能无法显示为一个action view,而是作为菜单项显示在overflow菜单中的。比如,应用程序如果运行在小屏幕上,Action Bar可能会没有足够的空间,无法在其它action项或浏览元素边上再显示搜索widget了,于是它就会作为菜单项显示在overflow 菜单中。如果放入了overflow菜单中,则它会表现为普通的菜单项,而不会显示为action view(搜索widget)。
为了应付这种状况,用户从overflow菜单中选中时,与搜索widget关联的菜单项应该激活搜索对话框。为了实现这一目标,必须实现onOptionsItemSelected()来处理“搜索”菜单项,并调用onSearchRequested()打开搜索对话框。
关于Action Bar中的项目如何工作及如何处理这种情况,请参阅Action Bar开发者指南。
关于同时使用对话框和widget的示例代码,请参阅支持检索的字典。
通过在搜索配置文件中添加android:voiceSearchMode属性,可以在搜索对话框或widget 中加入语音搜索功能。这会增加一个语音搜索按钮,用于启动语音提醒。当用户说完后,转译成文本的请求将会发送给搜索activity。
例如:
<?xml version="1.0" encoding="utf-8"?>
<searchablexmlns:android="http://schemas.android.com/apk/res/android"
</searchable>
值showVoiceSearchButton是开启语音搜索的必填项,而第二个值launchRecognizer标明语音搜索按钮应启动一个识别器返回转译过的文本,用于搜索activity 。
可以用其它一些属性来定义语音搜索的行为,比如所用的语言和返回结果的最大数量。关于支持的属性的详情,请参阅搜索配置文件。
注意:请仔细考虑一下应用程序是否适合使用语音搜索。所有用语音搜索键执行的的搜索都是立即提交给搜索activity 的,用户没有机会查看转译过的请求文本。请对语音识别过程进行充分的测试,确保对用户可能在本应用中提交的请求能作出正确的理解。
在Android系统的支持下,搜索对话框和搜索widget都能在用户键入时提供搜索建议项,系统将管理建议项列表并对用户选中建议项时触发的事件进行处理。
可以提供两种搜索建议项:
最近请求搜索建议
这类建议项很简单,即为用户之前在本应用程序中使用过的搜索请求文本。
参阅添加最近请求建议项。
自定义搜索建议
这类搜索建议来自于自定义数据源,可帮助用户迅速选中正确的拼写或所检索的内容。图3展示了一个字典应用中的自定义建议项示例——用户可以选中建议项以直接获取释义。
请参阅添加自定义建议项。
图3.带自定义搜索建议项的搜索对话框屏幕截图