第三部分:Android 应用程序接口指南---第二节:UI---第九章 搜索

第9章 搜索

在android平台上搜索是一个核心的用户功能。无论内容位于设备或网络上,用户应该能够搜索任何对它们可用的数据。为了创建一个一致的用户搜索体验,Android平台提供了一个搜索框架帮助你的应用程序实现搜索功能。搜索框架提供了两种模式的搜索输入:一个在屏幕的顶部搜索对话框或搜索小部件(SearchView),您可以将其嵌入到你的activity布局。在这两种情况下,Android系统将通过传递一个查询到特定的activity协助你实现搜索。下图9-1显示了一个示例搜索对话框和可选的搜索建议。



图9-1 一个搜索对话框的屏幕截图

一旦以设置好了搜索对话或者搜索widget,你就可以:

◆可以使用声音搜索

◆通过最近的搜索词提供搜索建议

◆提供自定义搜索建议,以匹配您的应用程序数据的实际结果

◆在系统范围的快速搜索框中提供你应用程序的搜索建议

注意:搜索框架不为你的数据提供API。搜索的话,你需要适合你的数据的API。例如,如果你的数据在SQLite数据库中,你应该使用android.database.sqlite执行搜索。同时,也不能保证每个设备都提供了一个专门的搜索按钮来调用搜索界面。当使用搜索对话框或一个自定义的接口,您必须提供一个搜索按钮在你的UI,激活了搜索界面。

9.1 创建一个搜索接口

当你准备为你的应用程序添加搜索功能时,Android会帮你助实现用户接口,这个接口是一个搜索对话框,显示在Activity窗口的顶端,或是一个你可以插入到你的布局之中的搜索widget。搜索对话框和widget都可以把的搜索请求传递给你的应用程序中一个指定的Activity。通过这种方法,用户可以在任何一个搜索对话框或可用的widget中发起一个搜索,并且系统将会启动一个适当的activity来实现这个搜索并呈现其结果。

其它搜索对话框和widget的可用功能有:

◆语音搜索

◆基于最近查询的搜索建议

◆在你的应用程序数据中,初建结果相匹配的搜索建议

9.1.1基础

在你开始之前,你应该先决定你是要用搜索对话框或是搜索widget来实现你的搜索接口。这两个方法提供了相同的搜索功能,但是存在一点小区别:

1. 搜索对话框是一个由Android系统控制的UI部件。当被用户激活时,搜索对话框出现在activity的顶部,如图9-2所示。Android系统控制了搜索对话框中的所用事件。当用户提交一个请求时,系统将请求传递给你指定用来处理搜索的activity。当用户输入时,对话框也可以提供搜索建议。

 

图9-2 应用程序的搜索对话框截图

2. 搜索widget是SearchView的实例化对象,你可以放在布局中的任意位置。默认的,搜索widget表现的如同一个标准的EditText widget一样,并不做其它任何事情,不过你可以更改其配置,让Android系统所有的输入事件,传递请求给合适的activity,并且提供搜索建议(与搜索对话框相同)。然而,搜索widget只在Android 3.0(API Level 11)以及更高版本中可用。

当用户从搜索对话框或一个搜索widget执行一个搜索时,系统创建了一个Intent对象,并保存了用户的请求。然后,系统启动了你声明的用来处理搜索的activity(“可搜索的activity”),并把intent对象传递给它。给你的应用程序设置一些东西来辅助搜索,以下是你需要做的:

◆一个可搜索的配置
一个XML文件,为搜索对话框或widget配置了一些设定。它包括了一些功能设定,如语音搜索,搜索建议,以及搜索框提示文本。

◆一个可搜索的activity
一个Activity,用来接收搜索查询,搜索你的数据并显示搜索结果。

◆一个搜索接口,有两种选择:

搜索对话框
默认的,搜索对话框是被隐藏的,但当用户按了设备的搜索按键(当可用时)或是其他用户接口中的按键时,它会出现在屏幕的顶部。

SearchView widget
使用搜索widget允许你把搜索框放置于activity的任意位置。而不用放在activity的布局之中,然而,一般情况下使用一个Action Bar中的操作视图将更加便于用户使用。

9.1.2创建一个可搜索的配置

首先,你需要一个叫做搜索配置的XML文件。它配置了某个UI用来接收搜索对话框或widget,并且定义了例如搜索建议和语音搜索等功能是怎么样实现的。这个文件一般被命名为searchable.xml,并且必须保存在res/xml/路径下。系统会使用这个文件来创建一个SearchableInfo对象的实例,但是,你并不能在运行时自己创建这个对象-而应该是在XML中申明这个可搜索的配置。这个可搜索的配置文件必须包含<searchable>这个元素作为其根结点,并指明一个或多个属性。例如代码清单9-1所示:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_label"
    android:hint="@string/search_hint" >
</searchable>

 

代码清单9-1

android:lable是唯一一个必要的属性。它指向了一个字符串资源,这个字符串应该是应用程序的名字。这个标签只有在你为快速搜索框启用了搜索建议时才会对用户可见。这时,这个标签将出现在系统设置中的可搜索项目列表中。虽然不要求,但我们建议你总是设置一个android:hint属性,用户输入请求之前,在搜索框中提供一个可提示性的字符串。这个提示很重要,因为它为用户搜索提供了重要的线索。为了和其它Android应用程序保持一致,你应该为你的android:hint字符串使用如下格式:"Search <content-or-product>"。如,"Search songs and artists" 或 "Search YouTube"。

<searchable>元素可接受几种属性。然而,你并不需要其中大多数的属性,直到你添加一些特性,如Search Suggestions和Voice Search。

9.1.3创建一个可搜索的Activity

一个可搜索的Activity是你应用程序中的一个Activity,它执行一个基于查询字符串的搜索,并呈现搜索结果。当用户在搜索对话框或widget中执行一个搜索时,系统将启动你的可搜索activity,同时把搜索请求通过intent对象的ACTION_SEARCH动作传送给它。你的activity通过intent对象的QUERY获得查询,然后搜索你的数据并展示结果。因为你可以在应用程序中的任何一个activity中包含搜索对话框或widget,所以系统必须知道哪一个activity是你的可搜索activity,这样他才能够准备的传递搜索查询。所以,你必须首先在Android的manifest文件中申明你的可搜索activity。

1. 声明一个可搜索活动

如果你还没有,那么先创建一个Activity来执行搜索并展现结果。你目前还不需要实现搜索功能-只是创建一个你可以用来在manifest中申明的activity。放在manifest的<activity>元素中:

◆在<intent-filter>元素中申明这个activity用来接收ACTION_SEARCH的intent对象。

◆在<meta-data>元素中指定一个用来使用的可搜索配置。

例如代码清单9-2所示:

<application ... >
    <activity android:name=".SearchableActivity" >
        <intent-filter>
            <action android:name="android.intent.action.SEARCH" />
        </intent-filter>
        <meta-data android:name="android.app.searchable"
                   android:resource="@xml/searchable"/>
    </activity>
    ...
</application>

 

代码清单9-2

<meta-data>元素必须包括android:name属性,并赋值"android.app.searchable"并且android:resource属性使用一个可搜索配置文件的引用(在这个例子中,指的是res/xml/searchable.xml文件)。<intent-filter>并不需要一个使用DEFAULT值的<category>节点(常见于<activity>节点之中),因为系统使用了其组件名,能把ACTION_SEARCH的intent对象准确的传递给你的可搜索activity。

2. 执行一个搜索

当你在manifest中申明了你的可搜索activity后,在可搜索activity中执行一个搜索包括以下三个步骤:

◆接收查询

◆搜索数据

◆显示结果

一般来说,你的搜索结果应该显示在ListView控件中,所以,你可能会希望你的可搜索activity继承ListActivity。它包含了一个默认的布局文件,其中有一个单独的ListView控件,并提供了几个方便的方法来使用ListView控件。

接收查询:当用户从搜索对话框或widget执行一个搜索时,系统将启动你的可搜索activity,并把ACTION_SEARCH的intent对象传递给它。这个intent对象携带了QUERY extra字符串中的搜索请求。当activity启动时,你必须检查这个intent对象,并获得这个字符串。以下例子为当你的可搜索activity启动时将怎样获得搜索请求,如代码清单9-3所示:

 @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search);
 
    // Get the intent, verify the action and get the query
    Intent intent = getIntent();
    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
      String query = intent.getStringExtra(SearchManager.QUERY);
      doMySearch(query);
    }
}

 

代码清单9-3

QUERY字符串始终包含了ACTION_SEARCH的intent对象。在这个例子中,请求被找到,并传递给一个本地方法doMySearch(),在这个方法中实现了实际的搜索操作。

搜索数据:对于你的应用程序,存储和搜索数据的过程是唯一的。你可以通过多种方法来实现储存和搜索数据,但本章没有教你怎么样存储并搜索数据。你需要根据你的需求和数据格式认真考虑怎么样存储和搜索数据。然而,以下是一些你可以会用到的:

如果你的数据储存在设备的SQLite数据库中,显示一个全文本搜索(使用FTS3,而不是LIKE查询),通过文本数据,能够提供一个更加稳健的搜索,并且可以显著的提高搜索时间。关于FTS3的信息请见http://sqlite.org/fts3.html,关于Android中SQLite的信息请见SQLiteDatabase类。同样可见SDK里面simple中的Searchable Dictionary范例应用程序,其完整的展示了SQLite如何通过FTS3实现搜索。

如果你的数据存储在网络上,那么搜索的表现将会由用户的数据连接所控制。如果你想在搜索结果返回之前显示一个旋转的进度条

显示结果:如前所述,推荐的用来展示搜索结果的UI为ListView,所以你可能会想要你的可搜索activity继承ListActivity。然后你可以调用一个setListAdapter()方法,把已绑定了数据的Adapter传递给它。

9.1.4使用搜索对话框

搜索对话框在屏幕上端提供了一个浮动的搜索框,并在其左侧使用了应用程序的图标。用户输入时,搜索对话框可以提供搜索建议,当用户执行一个搜索时,系统把搜索请求传给一个可搜索activity,并执行搜索。然而,如果你在为Android 3.0设备开发应用程序,你应用考虑使用搜索widget来代替搜索对话框。默认的,搜索对话框始终保持隐藏,直到用户激活它。如果用户的设备有一个搜索按钮,点击这个按钮将会默认的激活搜索对话框。你的应用程序也可根据要求,通过调用onSearchRequested()方法来激活搜索对话框。但是,这些在你没有为activity启动搜索对话框之前都是无效的。

启动搜索对话框,你必需向系统指出哪个可搜索activity应该从搜索对话框接收搜索请求,来执行搜索。例如,前面的内容一个名叫SearchableActivity被创建。如果你想要另一个单独的activity,取名为OtherActivity,来显示搜索对话框并把搜索传递给SearchableActivity。你必需要在manifest中申明SearchableActivity为OtherActivity中搜索对话框所使用的可搜索activity。

为一个activity的搜索对话框申明一个可搜索activity,在各自的activity节点<activity>中添加一个<meta-data>节点。这个<meta-data>必需包括android:value属性来指定可搜索activity类的名字和android:name属性,并使用"android.app.default_searchable"作为其值。

例如,以下为同时为可搜索activity,SearchableActivity,和另一个activity,OtherActivity,其通过搜索对话框使用了SearchableActivity来执行搜索,如代码清单9-4所示:

<application ... >
    <!-- this is the searchable activity; it performs searches -->
    <activity android:name=".SearchableActivity" >
        <intent-filter>
            <action android:name="android.intent.action.SEARCH" />
        </intent-filter>
        <meta-data android:name="android.app.searchable"
                   android:resource="@xml/searchable"/>
    </activity>
    <activity android:name=".OtherActivity" ... >
        <meta-dataandroid:name="android.app.default_searchable"
                   android:value=".SearchableActivity"/>
    </activity>
    ...
</application>

 

代码清单9-4

因为OtherActivity现在包含了一个<meta-data>节点,用来申明哪一个可搜索activity用来执行搜索,这个activity已经启用了搜索对话框。当用户使用这个activity时, onSearchRequested()方法将激活搜索对话框。当用户执行搜索时,系统启动SearchableActivity并传递ACTION_SEARCH的intent对象。可搜索activity默认自带了搜索对话框,并不需要把这个申明添加到SearchableActivity中。如果你想给应用程序中的每一个activity都提供搜索对话框,把以上提到的<meta-data>节点作为<application>节点的子元素插入,而不用在每一个<activity>中都添加一遍。通过这个方法,每一个activity都继承了这个值,提供搜索对话框,并把搜索传递给同一个可搜索activity。(如果你有多个可搜索activity,你可以复写默认的可搜索activity,在每一个activity中放置一个不同的<meta-data>申明)。现在,搜索对话框已对你的活activity启用,你的应用程序已经准备好可以执行搜索了。

1. 调用搜索对话框

在前文中已经提到,设备的搜索按钮可以打开搜索对话框,只要当前的activity在manifest中申明了可用的搜索activity。然而,一些设备并没有一个特定的搜索按钮,所以,你并不应该假设搜索按钮激活对话框始终可行。当使用搜索对话框时,你应该始终为你的UI提供另一个搜索按钮,可以调用onSearchRequested()方法来激活搜索对话框。例如,你要么在Options Menu中提供一个菜单项,要么在activity的布局中添加一个按钮来调用onSearchRequested()方法,并激活搜索。

你也可启用"type-to-search(键入搜索)"功能,这个功能在用户开始敲击键盘时激活搜索对话框。你可以在activity的onCreate()方法中通过调用setDefaultKeyMode(DEFAULT-KEYS-SEARCH-LOCAL)来启动"type-to-search(键入搜索)"功能。

2. 搜索对话框对Activity生命周期的影响

搜索对话框是一个漂浮于屏幕顶端的对话框。它并不影响activity的栈,所以当搜索对话框出现时,没有任何生命周期方法(如onPause())被调用。activity只是失去了输入焦点,因为输入焦点被交给了搜索对话框。如果你想在搜索对话框被激活时收到通知,复写onSearchRequested()方法。当系统调用这个方法时,将出现提示,你的activity失去了输入焦点,并交给了搜索对话框,所以你可以用来对事件做适当的处理工作(如暂停一个游戏)。如代码清单9-5所示:

@Override
public boolean onSearchRequested() {
    pauseSomeStuff();
    return super.onSearchRequested();
}

 

代码清单9-5

如果用户通过返回按键取消了搜索,搜索对话框被关闭,活动重新获得输入焦点。当搜索对话框关闭时,你可以使用setOndismissLister()和/或setOnCancelListener()方法来注册一个通知。你应该只需要注册OnDismissListener,因为它在每次搜索对话框关闭时都会被调用。OnCancelListener只有在用户明确的退出搜索对话框时才附属于这个事件,所以在搜索执行时被不会被调用(在这种情况下,搜索对话框将自然消失)。

如果当前的activity并不是可搜索activity,那么,在用户执行一个搜索时正常的activity生命周期事件将被触发(当前活动接收onPause()等方法)。然而,如果当前activity为可搜索activity,那么以下两者之一将发生:

◆默认的,可搜索activity接收ACTION_SEARCH的intent对象,调用onCreate()方法,并且一个新的activity实例被添加到activity栈顶。现在就有两个可搜索activity的实例在activity栈中(所以点击返回按钮将返回到前一个可搜索activity实例,而不是当前存在的可搜索活动)。

◆如果你设定android:launchMode为singleTop,那么可搜索activity接收ACTION_SEARCH的intent对象,调用onNewIntent(Intent) activity,传递新的ACTION_SEARCH的intent对象。例如,以下为,在可搜索activity启动模式为"singleTop"时,你应该怎么样处理这种情况,如代码清单9-6所示:

 @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.search);
    handleIntent(getIntent());
}
 
@Override
protected void onNewIntent(Intent intent) {
    setIntent(intent);
    handleIntent(intent);
}
 
private void handleIntent(Intent intent) {
    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
      String query = intent.getStringExtra(SearchManager.QUERY);
      doMySearch(query);
    }
}

 

代码清单9-6

与这个章节中关于Performing a Search范例中的代码对比,所有处理搜索intent对象的代码现在都包括在了handleIntent()方法中,所以,onCreate()和onNewIntent()都可以执行这个方法。

当系统调用onNewIntent(Intent)方法时,activity被没有被重新启动,所以getIntent()方法返回了与onCreate()方法接收到的相同的intent对象。这也是为什么你应该在onNewIntent(Intent)中调用setIntent(Intent)方法(这样,activity保存的intent对象被更新了,以防将来你调用getIntent())。

第二个场景使用"singleTop"启动模式是一般作法,因为,一旦搜索完成,其可为用户提供一个很好的来执行附加搜索,并且,如果你的应用程序创建了多个可搜索activity实例将是一个不好的体验。所以,我们建议我们建议你在应用程序的manifest中设置可搜索activity为"singleTop"启动模式。如代码清单9-7所示:

<activity android:name=".SearchableActivity"
          android:launchMode="singleTop" >
    <intent-filter>
        <action android:name="android.intent.action.SEARCH" />
    </intent-filter>
    <meta-data android:name="android.app.searchable"
                      android:resource="@xml/searchable"/>
  </activity>

 

代码清单9-7

3. 传递搜索上下文数据

在一些情况下,你可以为每一个搜索,在可搜索activity中为搜索请求添加必要的精练提纯。然则,如果你想要基于用户执行搜索的activity来精练搜索条件,你可以在系统传递给可搜索activity的intent对象中添加一些额外的数据。你可以在SearchManger.APP_DATABundle中传递额外的数据,其包含在ACTION_SEARCH的intent对象中。

传递这种类型数据给可搜索activity,在用户可执行搜索的activity中复写onSearchRequested()方法,使用额外数据创建一个Bundle对象,并调用Activity.startSearch()方法来激活搜索对话框。如代码清单9-8所示:

@Override
public boolean onSearchRequested() {
     Bundle appData = new Bundle();
     appData.putBoolean(SearchableActivity.JARGON, true);
     startSearch(null, false, appData, false);
     return true;
 }

 

代码清单9-8

返回"true",表明你已经成功的处理了这个回调事件,并调用了startSearch()方法激活搜索对话框。一旦用户确认了一个请求,它将伴随着你添加的数据一同传递给可搜索activity。你可以从APP_DATA的Bundle中提取额外的数据,来精练搜索。如代码清单9-9所示:

Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA);
if (appData != null) {
    boolean jargon = appData.getBoolean(SearchableActivity.JARGON);
}

 

代码清单9-9

注意:永远不要在onSearchRequested()回调方法外调用startSearch()方法。为了激活搜索对话框,始终调用onSearchRequested()方法。否则,onSearchRequested()不被调用并且定制(如上例中添加的appData)将丢失。

9.1.5使用搜索Widget

SearchView widget在Android 3.0和更高版本中可用。如果你在为Android 3.0设备开发应用程序,并且决定使用搜索widget,建议你把搜索widget当作动作条中的动作视图插入,而不是使用搜索对话框(也不是把搜索widget放到activity布局中)。例如,如图9-3展示了动作条中的搜索widget。

 

图9-3 在动作条中作为“action view”的SearchView widget

搜索widget提供了与搜索对话框相同的功能。当用户执行一个搜索时,它将启动适当的activity,并提供了搜索建议和语音搜索。当你把搜索widget当作一个action view使用时,你依然需要支持使用搜索对话框,以防搜索widget不适合动作条。

1. 配置搜索小插件

如前所述,在创建了可搜索配置和可搜索Activity之后,你需要为每一个SearchView启用辅助搜索。你可以通过调用setSearchableInfo()并传递表示了可搜索配置的SearchableInfo对象来实现。

你可以通过SearchManager调用getSearchableInfo()方法来获得一个SearchableInfo的引用。

例如,如果你使用一个SearchView作为Action Bar中的一个action view,你应该在onCreateOptionsMenu()回调方法中启用widget,如代码清单9-10所示:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
   
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.options_menu, menu);
 
    SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
    searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
    searchView.setIconifiedByDefault(false); 
 
    return true;
}

 

代码清单9-10

这是所有你需要的。搜索widget已配置,并且系统将把搜索请求传递给可搜索activity。你可以为搜索小插件启用搜索建议。如果你想自己处理所有的输入,你可以使用一些回调方法和事件监听器来实现。更多信息请见SearchView API和相关的接口。

2. 其他搜索widget的特性

SearchView widget允许了一些你可能想要的附加功能:

◆一个确认按钮

默认的,没有按钮来确认一个搜索请求,所以用户必须点击键盘上的“返回”键来发起一个搜索。你可以通过调用setSubmitButtonEnabled(true)来添加一个“确认”按钮。

◆细化的查询搜索建议

当你启用搜索建议时,通常情况下,你期望用户简单的选择一个建议,但他们也许会想要细化建议的搜索请求。你可以通过调用setQueryRefinementEnabled(true)方法,在每一个建议边上添加一个按钮,其在搜索框中插入了为用户细化所用的建议。

◆搜索框可见性的开关能力

默认情况下,搜索widget是“图标化的”,即它代表一个搜索图标(一个放大镜),当用户点击时将放大来展示搜索框。如前所述,你可以通过默认方式显示搜索框,通过调用setIconifiedByDefault(false)。你也能通过调用setIconified()来切换搜索widget的外观。

SearchView类中还有一些其他的APIs,允许你定制搜索widget。然而,大部分只适用于你自己处理所有用户输入,而不是使用Android系统来传递搜索查询和显示搜索建议。

3. 同时使用widget和对话框

如果你把搜索widget当作action view插入到动作条中,并且让其在动作条时显示“if there is room”(通过设定android:showAsAction="ifRoom"),那么,有一定机会搜索widget不会作为一个action view出现,但是,菜单选项将会出现在溢出菜单中。例如,当你的应用程序在一个更小的屏幕上运行,状态条中可能没有足够的空间来显示搜索widget和其它的动作项目或导航元素,所以,菜单项将会代替其显示在溢出菜单中。当显示在溢出菜单时,这个项目就如同普通菜单项一样工作,并且不会显示action view(搜索widget)。

出下这种情况,当用户从溢出菜单选择你用来存储搜索widget的菜单项应该激活搜索对话框。为了实现这个功能,你必须实现onOptionsItemSelected()方法来处理搜索菜单项并且通过调用onSearchRequested()方法来开启搜对话框。

9.1.6添加语音搜索

你可以在搜索widget听对话框中加入语音搜索功能,通过在搜索配置中添加android:voiceSearchMode属性。这个方法添加了一个语音搜索按钮,启动一个语音提示信息。当用户结束语音,转录搜索请求被传给了可搜索activity。如代码清单9-11所示:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/search_label"
    android:hint="@string/search_hint"
    android:voiceSearchMode="showVoiceSearchButton|launchRecognizer" >
</searchable>

 

代码清单9-11

showVoiceSearchButton值用来启用语音搜索,第二个值,launchRecognizer,指定了语音搜索按钮应该启动一个识别器,将转录的文本返回给搜索activity。

你可以提供其它的属性来指定语音搜索行为,如希望使用的语言和最大的结果返回数。更多关于可用属性的信息,请见Searchable Configuration。仔细考虑语音搜索是否适用于你的应用程序。所有由语音搜索按钮执行的搜索将立即传递给搜索Activity,并不留机会给用户回顾转录的请求。有效的测试语音识别,并保证其正确理解用户在应用程序中确认的查询的类型。

9.1.7添加搜索建议

当用户输入时,在Android系统的帮助下,搜索对话框和widget都可以提供搜索建议。系统管理着搜索列表并且处理用户选择一个建议时的事件。

你可以提供两种类型的搜索建议:

近期请求搜索建议

这些建议是用户近期在应用程序中使用的搜索请求。

定制搜索建议

这些搜索建议是你从你的数据资源中提供的,用来帮助用户快速选择正确的拼写或项目进行搜索。图9-3展示了一个为字典定制搜索建议的例子——用户可以选择一个建议,并立即进入其解释。


图9-4 使用定制搜索建议的搜索对话框截图

9.2 添加最近的查询建议

当使用Android搜索对话框或者搜索widget时,你可以提供基于最近搜索查询的搜索建议项。举个例子,如果一个用户之前搜索过 "puppies,"那么一旦他或她开始键入同一个查询的时候这个查询将作为一个建议项展示出来。

9.2.1基础

最近搜索建议项只是被保存下来的搜索。当用户选择了其中一个建议项,你的搜索Activity会收到一个带有该Activity已经处理过的搜索查询的ACTION_SEARCH的intent

要提供搜索建议项,你需要:

◆实现一个搜索的Activity,如在本章9.1中描述的那样。

◆创建一个继承自SearchRecentSuggestionsProvider的content provider并在manifest中声明它。

◆修改搜索配置中有关提供搜索建议项的content provider的信息。

◆每次搜索执行的时候把查询内容保存到你的content provider中。

正如Android系统显示搜索对话框那样,它也同样在对话框或者搜索widget下显示搜索建议项。你所需要做的是提供一个系统能从中取得建议项的来源。

 

当系统识别到你的Activity是用于搜索的并且提供了搜索建议项,一旦用户开始键入查询的时候以下步骤就会发生:

◆系统取得搜索查询文本(不管当前键入了什么)并对包含你搜索建议项的content provider进行一次查询。

◆你的content provider返回一个Cursor,该Cursor指向所有与查询文本匹配的搜索建议项。

◆系统以列表形式展现该Cursor提供的建议项。

一旦最近搜索建议项展示出来,以下事情可能会发生:

◆如果用户键入了另外一个关键字,或者以任何形式改变了查询内容,系统将会重复上述步骤并更新搜索建议项列表。

◆如果用户执行了搜索,这些建议项将会被忽略,并且系统会使用一般的ACTION_SEARCH的intent将该搜索传递到你的搜索Activity中。

◆如果用户选择了其中一个建议项,一个ACTION_SEARCH的intent将会被传递到你的搜索Activity中,使用被选中的建议文本作为查询。

你的content provider继承的SearchRecentSuggestionsProvider类自动地完成了上述的工作,所以实际上你需要编写的代码很少。

9.2.2创建一个Content Provider

为了最近搜索建议项你所需的那个content provider必须是SearchRecentSuggestionsProvider的一个实现。这个类几乎为你做了所有的事情。你所需要做的只是写一个执行一行代码的类构造器。 举个例子,这是一个作为最近搜索建议项的content provider的一个完整的实现。如代码清单9-12所示:

public class MySuggestionProvider extends SearchRecentSuggestionsProvider {
    public final static String AUTHORITY = "com.example.MySuggestionProvider";
    public final static int MODE = DATABASE_MODE_QUERIES;
 
    public MySuggestionProvider() {
        setupSuggestions(AUTHORITY, MODE);
    }
}

 

代码清单9-12

setupSuggestions()方法的调用传递了搜索权限的名字和数据库模式。搜索权限可以是任何唯一的字符串,但最好的做法是使用你content provider的全路径(类名跟随在包名后面;举个例子,“com.example.MySuggestionProvider”)。数据库模式必须包含DATABASE_MODE_QUERIES并可以选择性的包含DATABASE_MODE_2LINES,这个模式在你的建议项表中增加了另一列,允许你为每个建议项提供第二行文本。举个例子,如果你想为每个建议项提供两行文本,可以这么写,如代码清单9-13:

public final static int MODE = DATABASE_MODE_QUERIES | DATABASE_MODE_2LINES;

 

代码清单9-13

现在在你的manifest中使用与你SearchRecentSuggestionsProvider类(和搜索配置)中同样的权限字符串来声明你的content provider。如代码清单9-14所示:

 <application>
    <provider android:name=".MySuggestionProvider"
              android:authorities="com.example.MySuggestionProvider" />
    ...
</application>

 

代码清单9-14

9.2.3修改可用搜索配置

为了配置系统去使用你的建议项provider,你需要在你搜索配置的<searchable>节点内添加 android:searchSuggestAuthority 和 android:searchSuggestSelection 属性。如代码清单9-15所示:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_label"
    android:hint="@string/search_hint"
    android:searchSuggestAuthority="com.example.MySuggestionProvider"
    android:searchSuggestSelection=" ?" >
</searchable>

 

代码清单9-15

android:searchSuggestAuthority的值应该是你content provider的全路径名,这个名字必须完全匹配在content provider中使用的权限(上面例子中的AUTHORITY字符串)。 android:searchSuggestSelection的值必须是前缀为一个空格的一个单一的问号,(" ?"),这是SQLite选择参数的一个简单的占位符(会被用户输入的查询文本自动替换)。

9.2.4保存查询

要填入你的最近查询的集合,在你搜索Activity中接收到的每个查询,把它们添加到你的SearchRecentSuggestionsProvider中。要做这个事情,每次你的搜索Activity接收到一个查询的时候创建一个SearchRecentSuggestions的实例并调用saveRecentQuery()。如代码清单9-16所示:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
 
    Intent intent  = getIntent();
 
    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
        String query = intent.getStringExtra(SearchManager.QUERY);
        SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
                MySuggestionProvider.AUTHORITY, MySuggestionProvider.MODE);
        suggestions.saveRecentQuery(query, null);
    }
}

 

代码清单9-16

SearchRecentSuggestionsProvider构造函数需要与你在content provider中声明的一样的权限和数据库模式。saveRecentQuery()取搜索查询字符串作为第一个参数并可选的用第二个参数来包含建议项的第二行数据(或者null)。第二个参数仅在你用DATABASE_MODE_2LINES来为你的搜索建议项启用双行模式的情况下使用。如果你启用了双行模式,那么当系统在寻找匹配的建议项时,这个第二行也会被用作查询文本的匹配。

9.2.5除建议数据

为了保护用户的隐私,你应该总是提供一个方法可以让用户能清除最近搜索建议项。要清除查询历史,调用clearHistory()。如代码清单9-17所示:

SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
        HelloSuggestionProvider.AUTHORITY, HelloSuggestionProvider.MODE);
suggestions.clearHistory();

 

代码清单9-17

选择一个“清除搜索历史”的菜单项或设置项或按钮来执行这段代码。你也应该提供一个确认对话框来确认用户是否真要删除他们的搜索历史。

9.3 添加自定义建议

当使用Android搜索对话框或者搜索widget时,你可以提供自定义搜索建议项,这些建议项从你的应用程序数据中创建出来。举个例子,你的应用程序是一个字典,你可以把字典中那些与当前输入文本相匹配的单词作为建议项。那些是最有价值的建议项,因为你可以高效地预测到用户所需并提供访问它的实例。图9-4展示了一个带自定义建议项的搜索对话框。一旦你提供了自定义搜索建议项,你同样可以使它们对系统范围的Quick Search Box有效,提供在你应用程序外访问你内容的方式。 在你开始使用这个教程去增加自定义建议项之前,你需要已经实现了你应用程序搜索所需的搜索对话框和搜索widget。

 

图9-5 使用自定义搜索建议项的截图

9.3.1基础

当用户选择了一个自定义建议项,Android系统会发送一个Intent到你的搜索Activity中。鉴于一个普通的搜索查询发送一个带ACTION_SEARCH action的intent,你反而可以限定你的自定义建议项使用ACTION_VIEW(或者其它intent action),并可以包含跟选中建议项相关的数据。回到字典的例子上来,当用户选择了一个建议项,你的应用程序可以马上打开那个单词的定义,而不是在字典中搜索那些匹配项。要提供自定义建议项,要做以下事情:

◆实现一个基本的搜索Activity。

◆修改搜索配置中关于提供自定义建议项的content provider的信息。

◆为你的建议项创建一个表(比如在一个SQLiteDatabase中),并用所需要的列来格式化这个表。

◆创建一个Content Provider来访问你的建议项表并在你的manifest文件中声明这个provider。

◆定义当用户选择一个建议项时将会被发送的Intent的类型(包括自定义的action和自定义的data)。

就像Android系统显示搜索对话框那样,它同样可以显示你的搜索建议项。你所需要的是一个系统能从中取得你建议项的content provider。 当系统识别到你的Activity是可用于搜索的并提供了搜索建议项,当用户键入查询时以下步骤将会发生:

◆系统会取得搜索查询文本(不管当前键入了什么)并对管理你的建议项的content provider执行一次查询。

◆你的content provider返回一个指向跟搜索查询文本相关的所有建议项的Cursor。

◆系统展示这个Cursor提供的建议项的列表。

一旦自定义建议项被展示出来,以下情况可能会发生:

◆如果用户键入另外一个关键字,或者以任何方式改变了查询内容,上述的步骤将会被重复,并且建议项列表也会得到适当的更新。

◆如果用户执行了搜索,建议项会被忽略并且系统会使用一般的ACTION_SEARCH的intent来将搜索传递到你的搜索Activity中去。

◆如果用户选择了一个建议项,一个携带自定义action和自定义data的intent将会被发送到你的搜索Activity中,这样你的应用程序就可以打开建议的内容。

9.3.2修改搜索配置

要支持自定义建议项,在你搜索配置文件的<searchable>节点下加入android:searchSuggestAuthority属性。如代码清单9-18所示:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_label"
    android:hint="@string/search_hint"
    android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider">
</searchable>

 

代码清单9-18

你可能需要一些额外的属性,这取决于你关联到每个建议项的intent类型和你想如何将查询格式化至你的content provider。

9.3.3创建一个Content Provider

为自定义建议项创建一个content provider需要先掌握有关content provider的知识。就一个自定义建议项的content provider大部分而言跟任意其他的content provider都是一样的。但是,对你提供的每个建议项来说,Cursor中各行必须包含指定的列,这些列是系统能够理解的并使用来格式化建议项的。

当用户开始往搜索对话框和搜索部件中键入内容时,系统会在每次一个字母被键入时调用query()来查询你建议项的content provider。在你query()方法的实现中,你的content provider必须查询你的建议项数据并返回一个Cursor,这个Cursor指向你觉得是好建议的那些行。 有关为自定义建议项创建一个content provider的详细信息会在以下两部分讨论:

1. 处理建议项请求

当系统从你的content provider中请求建议项的时候,会调用你content provider的query()方法.你必须实现这个方法来查询建议项数据并返回一个指向你认为有关的建议项的Cursor。

以下是系统传递给你query()方法的参数概要(按顺序排列):

uri

总是一个content类型的Uri,格式如下:

content://your.authority/optional.suggest.path/SUGGEST_URI_PATH_QUERY

系统默认的行为是传递这个URI并在URI后面拼接上查询文本。如:

content://your.authority/optional.suggest.path/SUGGEST_URI_PATH_QUERY/puppies

末尾的查询文本是使用URI编码规则编码过的,所以你可能需要在执行查询之前解码它。

只有当你在你的搜索配置文件中为android:searchSuggestPath属性设置了路径的时候才需要optional.suggest.path这个部分。只有多个搜索activity需共用同一个content provider时,才需要用到这个部分,这种情况下,你需要区分建议项请求的来源。

注意:SUGGEST_URI_PATH_QUERY并不属于URI提供的字符串,而是你要指向此路径所需的常量。

projection

总为null

selection

该值由你搜索配置文件中的android:searchSuggestSelection属性提供,如果你没声明android:searchSuggestSelection这个属性则该值为null。

selectionArgs

如果你在你的搜索配置中声明了android:searchSuggestSelection的属性,那么这个数组类型的参数的第一个(也只有一个)元素包含了搜索请求文本。如果没声明的话,则这个参数为null。

sortOrder

总为null

系统可以使用两种方式来发送你的请求。默认的方式是把请求文本包含在作为uri参数传递的content URI的最末尾。但是,如果你搜索配置文件的android:searchSuggestSelection属性包含了selection的值,那么系统将会使用selectionArgs字符串数组的第一个元素来传递请求文本。这两种方式都会在后面说明。

从Uri中获取请求文本

请求文本默认地是附在uri参数(一个Uri对象)的最后一段上。在这种情况下要获取请求文本,简单的使用getLastPathSegment()方法就可以了。比如:

String query = uri.getLastPathSegment().toLowerCase();

 

这个方法返回Uri的最后一段,也就是用户输入的请求文本。

从selection参数中获取请求文本

相对于使用Uri这种方式获取请求文本,你可能会觉得让你的query()方法接收它执行查询所需的所有内容会更为合理,并且你希望selection和selectionArgs能携带合适的值。在这种情况下,用你SQLite语句的selection字符串来添加android:searchSuggestSelection到你的搜索配置文件中。selection字符串中包含一个作为占位符的问号 ("?")来代表实际要搜索的请求文本。如果你选择接受建议查询这种方式,需要添加通配符来查询文本,append(和/或前缀)他们的selectionArgs参数,因为这个值包裹在引号并插入到?号的地方。以上例子中另一个新的属性就是android:searchSuggestIntentAction,当用户选择一个建议时,它定义了intent和每一个要发送intent的action。如果你不想要在android:searchSuggestSelection属性中定义一个选择条款,但仍然想要在selectionArgs参数中接收查询文本的话,简单地提供一个非空值为android:searchSuggestSelection属性。它会通过selectionArgs触发一个查询并且你能忽略selection参数。通过这种方式,你可以不用定义实际的selection条款,这样你的content provider不需要处理它

9.3.4为建议项声明一个Intent

1. 声明intent action

给一个自定义的建议项添加最常见的intent action是ACTION_VIEW,当你想打开一些东西,就像一个单词的定义那样,比如联系人信息或者web页面。然而,intent action可以使任意其他action,甚至可以是让每一条建议都对应不同的action。你是否想要所有的建议都使用相同的intent action,你可以使用两种方法定义action:

◆在searchable配置文件中使用android:searchSuggestIntentAction属性,如代码清单9-19所示:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_label"
    android:hint="@string/search_hint"
    android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider"
    android:searchSuggestIntentAction="android.Intent.action.VIEW" >
</searchable>

 

代码清单9-18

◆使用SearchManager.SUGGEST_COLUMN_INTENT_ACTION为个别的建议定义action。添加SearchManager.SUGGEST_COLUMN_INTENT_ACTION列到你的建议表中。

当然你也可以结合这两种技术,例如你能包含android:searchSuggestIntentAction属性作为所有建议的默认action,然后为某个特别的建议重写这个action。如果在searchable配置文件中你不包含android:searchSuggestIntentAction属性,那么你必须为每一个建议包含SUGGEST_COLUMN_INTENT_ACTION列。

2. 声明intent data

当用户选择一个建议,你的可搜索Activity接收intent和已经定义的action,但是intent必须也携带数据为了让你的Activity来识别哪些建议被选中。具体来说,对于每一条建议数据应该是唯一,如行ID的建议在你的SQLite表,具体来说,数据应该是独特的每个建议,如在你的SQLite表中行ID的建议。当intent被接收后,你能用getData()或者getDataString()来取得附加的数据。并且可以使用以下两种方式定义数据:

◆在建议表的SUGGEST_COLUMN_INTENT_DATA列中为每一个建议定义数据。

在建议表中为每一个intent提供所有必要的数据信息,然后为每一行使用唯一的数据填充它。这个数据从这列中附加到intent就是你在这列中定义它。你能用getData()或getDataString()取得它。可以参考SDK simples下的Searchable Dictionary

◆分裂一个数据URI到两部分:一部分通常的建议和一部分每个唯一的建议。例如下面的通用建议可以使用android:searchSuggestIntentData属性,如代码清单9-19所示:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_label"
    android:hint="@string/search_hint"
    android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider"
    android:searchSuggestIntentAction="android.intent.action.VIEW"
    android:searchSuggestIntentData="content://com.example/datatable" >
</searchable>

 

代码清单9-19

在建议表的SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID列中为每个建议(唯一的部分)包括最后的路径。当用户选择一个建议时,系统从android:searchSuggestIntentData下获得字符串,附加一个(“/”)然后从SUGGEST_COLUMN_INTENT_DATA_ID列添加各自的值,直到形成一个完整的内容的URI。最后你能使用getData()取得Uri。

3. 添加更多数据

如果你需要用intent表示更多的信息,那么你能添加另外表中的列SearchManager. SUGGEST_COLUMN_INTENT_EXTRA_DATA。它可以存储有关建议的额外信息。数据保存在这列的intent的extra Bundle的SearchManager.EXTRA_DATA_KEY中 。

9.3.5处理Intent

现在你可以用自定义intent来提供自定义搜索建议,当用户选择一个建议时,你需要你的searchable activity来处理这些intents。除了处理Intent.Action_SEARCH之外,如代码清单9-20所示:

Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
    // 处理正常搜索查询的情况
    String query = intent.getStringExtra(SearchManager.QUERY);
    doSearch(query);
} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
    // 处理一个建议的点击 (因为这些建议都使用的是ACTION_VIEW)
    Uri data = intent.getData();
    showResult(data);
}

 

代码清单9-20

在上面这个例子中,intent action 是ACTION_VIEW并且数据携带了一个完成的URI指向建议的item,通过 android:searchSuggestIntentData字符串和SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID列的综合。这URI然后通过本地的showResult()方法查询URI指定的item。注意:你不需要添加intent filter到你的manifest文件中。系统通过名称传递到建议的intent来打开你的可搜索activity。所以activity不需要声明需要接收的action。

9.3.6重写查询文本

如果用户通过建议列表定向控制导航(如方向键或轨迹球),默认情况下,查询文本不更新。但你也能临时重写用户的查询文本。你可以重写查询文本有以下几种方法:

◆添加android:searchMode=queryRewriteFromText到你的可搜索配置中。在这种情况下,从建议的SearchManager.SUGGEST_COLUMN_TEXT_1列的内容用于重写查询文本 

◆添加android:searchMode= queryRewriteFromData到你的可搜索配置中。在这种情况下,从建议的SearchManager.SUGGEST_COLUMN_INTENT_DATA列的内容用于重写查询文本。它应该仅用于URI或其他想要用户可见的数据格式,如Http URL。内部的URI schemes(方案)不应该以这样的方式用于重写查询。

◆在你建议表中的SearchManager.SUGGEST_COLUMN_QUERY列提供唯一的查询文本字符串。如果这个列是现在的并且包含一个当前建议的值,那么用它来重写查询文本要么覆盖以前的实现。

9.4 搜索配置

为了在Android系统的协助下(把搜索查询传递到Activity中并提供搜索建议项)实现搜索,你的应用必须以一个XML文件的形式提供给系统一个搜索配置。

文件路径:

res/xml/filename.xml

Android使用文件名作为资源ID。

语法(代码清单9-21):

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="string resource"
    android:hint="string resource"
    android:searchMode=["queryRewriteFromData" | "queryRewriteFromText"]
    android:searchButtonText="string resource"
    android:inputType="inputType"
    android:imeOptions="imeOptions"
    android:searchSuggestAuthority="string"
    android:searchSuggestPath="string"
    android:searchSuggestSelection="string"
    android:searchSuggestIntentAction="string"
    android:searchSuggestIntentData="string"
    android:searchSuggestThreshold="int"
    android:includeInGlobalSearch=["true" | "false"]
    android:searchSettingsDescription="string resource"
    android:queryAfterZeroResults=["true" | "false"]
    android:voiceSearchMode=["showVoiceSearchButton" | "launchWebSearch" | "launchRecognizer"]
    android:voiceLanguageModel=["free-form" | "web_search"]
    android:voicePromptText="string resource"
    android:voiceLanguage="string"
    android:voiceMaxResults="int"
    >
    <actionkey
        android:keycode="KEYCODE"
        android:queryActionMsg="string"
        android:suggestActionMsg="string"
        android:suggestActionMsgColumn="string" >
</searchable>

 

代码清单9-21

XML节点:

<searchable>

定义所有Android系统用于提供辅助搜索的搜索配置。

属性:

android:label

字符串资源。(必须的。)你应用的名称。它应该跟你manifest文件中<activity>或者<application>节点中android:label属性的名称一样。这个标签只有当你设置android:includeInGlobalSearch为true的时候才对用户可见,在这种情况下,这个标签作为系统搜索设置中的一个可搜索项用来辨别你的应用。

android:hint

字符串资源。(推荐的。)当搜索框文本区域没有文本输入时显示的文本。它提示用户什么内容是可搜索的。为了跟其他Android应用保持一致性,你应该用"搜索 <内容-或者-产品>"这种格式来格式化android:hint的字符串。比如,"搜索歌曲或者艺术家"或者"搜索YouTube"。

android:searchMode

关键字。设置额外的模式来控制搜索的表现。当前可用的模式定义了当自定义建议项获取到焦点时搜索文本该如何被改写。以下的模式值是可接受的:

描述

"queryRewriteFromText"

使用SUGGEST_COLUMN_TEXT_1这一列的值来重写查询文本。

"queryRewriteFromData"

使用SUGGEST_COLUMN_INTENT_DATA这一列的值来重写搜索文本。这应该仅用于当SUGGEST_COLUMN_INTENT_DATA里面的值对用户的检查和编辑是合适的,通常HTTP URI的就是这样。

android:searchButtonText

字符串资源。显示在搜索按钮上的文本。按钮默认的展示一个搜索图标(放大镜),这对国际化来说是理想的,所以你不应当使用这个属性来改变按钮的图标,除非按钮执行的行为是搜索以外的东西(比如Web浏览器中的一个URI请求)。

android:inputType

关键字。定义了使用的输入法的类型(比如软键盘的类型)。对于大多数对期望输入的文本没有限制的搜索来说,你不需要用到这个属性。在inputType里查看这个属性的合法值。

android:imeOptions

关键字。为输入法提供额外的选项。对于大多数对期望输入的文本没有限制的搜索来说,你不需要用到这个属性。默认输入法的执行键值是"actionSearch"(在软键盘上提供一个"搜索"按钮来代替回车符)。在imeOptions里查看这个属性的合法值。

 

1. 搜索建议项的属性

如果你定义了一个content provider来生成搜索建议项,你需要定义额外的属性来配置与content provider的通讯。当提供搜索建议项的时候,你需要以下<searchable>属性的一部分:

android:searchSuggestAuthority

字符串。(提供搜索建议项必须的。)这个值必须跟Android manifest中<provider>元素里android:authorities属性提供的鉴权字符串一致。

android:searchSuggestPath

字符串。这个路径被用作建议项查询Uri的一部分,位于Uri的前缀和authority之后,但在标准的建议项路径之前。只有当你仅用一个content provider来处理不同类型的建议项(比如不同的数据类型)时会要用到这个属性,并且当你接收到这些建议项查询的时候你要有方式来区分它们。

android:searchSuggestSelection

字符串。这个值将作为selection参数传递到你的查询函数中。通常这对你的数据库来说是一个WHERE语句,并且应该包含一个单独的问号,这个问号是用户实际键入查询文本的占位符(比如"query=?")。当然你也可以通过在selectionArgs参数中使用任意非空值来触发查询文本的传递(然后忽略selection参数)。

android:searchSuggestIntentAction

字符串。当用户点击自定义搜索建议项时默认使用的intent action(比如"android.intent.action.VIEW")。如果这个值没有被选中的建议项覆盖(通过SUGGEST_COLUMN_INTENT_ACTION这一列),当用户点击一个建议项的时候这个值将会被赋给Intent的action字段。

android:searchSuggestIntentData

字符串。当用户点击自定义搜索建议项时默认使用的intent data。如果这个值没有被选中的建议项覆盖(通过SUGGEST_COLUMN_INTENT_DATA这一列),当用户点击一个建议项的时候这个值将会被赋给Intent的data字段。

android:searchSuggestThreshold

整型。触发建议项查询的最小字符数。仅保证系统在输入字符数小于这个阀值的情况下不会查询你的content provider。默认值是0。

 

2. Quick Search Box属性

要让你的自定义搜索建议项对Quick Search Box可用,你需要以下<searchable>属性的一部分:

android:includeInGlobalSearch

布尔值。(对在Quick Search Box里提供搜索建议项来说是必须的。)如果你想让你的建议项被包括在可全局访问的Quick Search Box里,把这个值设为"true"。在你的建议项能在Quick Search Box里显示之前,用户仍然必须在Quick Search Box的设置里面启用你的应用作为一个可搜索项。(译者注:就是说设置这个值为"true"的情况下用户还要在Quick Search Box的设置里面勾选上你的应用,这样才能在Quick Search Box里显示你的建议项。)

android:searchSettingsDescription

字符串。为你提供给Quick Search Box的搜索建议项提供一个简要说明,它会显示在(Quick Search Box设置里面)你应用对应的可搜索项条目中。你的描述应该扼要地描述出什么内容是可搜索的。比如,用“艺术家,唱片,专辑”来描述音乐应用可搜索的内容,或者用“已保存的笔记”来描述记事本应用可搜索的内容。

android:queryAfterZeroResults

布尔值。如果你希望对之前返回0个结果的搜索的超集仍然调用你的content provider,设置这个值为"true"。比如你的content provider对"bo"返回0个结果,那么它会对"bob"重新进行查询。如果这个设为"false",在这个单独的会话里超集将会被忽略("bob"不会调用一个新的查询)。这仅在搜索对话框的生存期或者使用search widget的activity的生存期中持续(当搜索对话框或者activity重新打开的时候,"bo"将再次查询你的content provider)。默认值是false。

3. 语音搜索属性

要启用语音搜索,你需要以下<searchable>属性的一部分:

android:voiceSearchMode

关键字。(对提供语音搜索功能来说是必要的属性。)用指定的模式启用语音搜索。(设备可能不提供语音搜索功能,在这种情况下这些标志将不起作用。)以下是可接受的模式值:

描述

"showVoiceSearchButton"

如果语音搜索在设备上可用,那么显示一个语音搜索的按钮。如果设置了这个模式,那么"launchWebSearch"和"launchRecognizer"两者之一也必须同时被设置

"launchWebSearch"

语音搜索按钮把用户直接带到一个内建的语音web搜索activity。大多数应用不需要这个标志,因为它将用户带离了调用搜索的activity。

"launchRecognizer"

语音搜索按钮把用户直接带到一个内建的录音activity。这个activity提示用户说话,解码语音文本,讲得到的查询文本转发到搜索activity,就好像用户在search UI输入查询文本然后按下搜索按钮一样。

android:voiceLanguageModel

关键字。语音识别系统应该使用的语言模式。以下是可接受的模式值:

描述

"free_form"

为口述的查询使用"自由形式"("free-form")的语音识别。这主要是为英语优化的

"web_search"

为更短的,类似检索的短语使用网页检索词识别。这种模式支持的语言比"free-form"的更多

android:voicePromptText

字符串。显示在语音输入框里的附加信息。

android:voiceLanguage

字符串。期望的语言,用Locale里面的字符串常量来表示(比如"de"表示德国或者"fr"表示法国)。只有期望的语言跟Locale.getDefault()的当前值不一样的时候才会需要这个属性。

android:voiceMaxResults

整型。强制返回结果的最大个数,其中包含用于ACTION_SEARCH的intent的主查询的"最佳"结果,这个结果总是会被提供。这个值必须大于等于1。使用RecognizerIntent .EXTRA_RESULTS从intent里面获取结果。如果没有提供该值,语音识别程序将决定返回多少个结果。

<actionkey>

为搜索action指定一个设备的按键和行为。当触摸设备屏幕上的按钮时,搜索action会根据当前查询文本或者获得焦点的建议项来触发一个特定的行为。比如联系人应用在提供了联系人建议项的时候,按下Call按键会提供一个搜索action来初始化一个通话,通话的对象就是当前获得焦点的联系人建议项所对应的联系人。不是所有的action键值在任何设备上都是可用的,也不是所有的键值都允许用这种方式来覆盖。比如"Home"键不能被用作action key并且按下它的时候必须返回home界面。同时要确保不要为用于输入查询文本的按键定义action key。这从根本上限定了action keys只能用于拨号键和菜单键。同时也要注意action keys通常不容易被发现,所以你不应该把它们作为核心用户功能提供出来。你必须通过指定android:keycode键值来指定一个按键,并且指定其他三个属性中至少一个的值来定义搜索action。

属性:

android:keycode

字符串。(必须的。)KeyEvent中定义的键值(比如"KEYCODE_CALL"),用来代表你所希望响应的那个action key。这个键值被附加到将被传递到你搜索activity中的ACTION_SEARCH的intent中。要查看这个键值,使用getIntExtra(SearchManager.ACTION_KEY)。不是所有的按键都支持搜索action,因为他们很多都被用作输入,导向和系统功能。

android:queryActionMsg

字符串。当用户正在输入查询文本时按下action key所发送的action信息。这个值被附加到ACTION_SEARCH的intent中,这个intent将会被系统传递到你的搜索activity中去。要查看这个值,使用getStringExtra(SearchManager.ACTION_MSG)。

android:suggestActionMsg

字符串。当一个建议项获得焦点时按下action key所发送的action信息。这个值被附加到intent中,系统将这个intent(使用你为之前为建议项定义的action)传递到你的搜索activity中去。要查看这个值,使用getStringExtra(SearchManager.ACTION_MSG)。这仅用于你所有的建议项都支持这个action key的情况下。如果不是所有建议项都能处理同一个action key的话,你应该下面的android:suggestActionMsgColumn属性替代。

android:suggestActionMsgColumn

字符串。你content provider中的列名,用于指定当一个建议项获得焦点时用户按下action key所发送的对应于该action key的action信息。这个属性能让你在逐个建议项的基础上操控action key,这是因为与使用android:queryActionMsg属性为所有的建议项定义一个action信息不同,使用这个属性你content provider每个条目都提供自身对应的一个action信息。

首先,你必须在你content provider中定义一列来让每个建议项都提供一个action信息,然后在这个属性中提供这个列名。系统查看你的建议项cursor,使用这个属性提供的字符串找到你action信息的那一列,然后从cursor中找出action信息的字符串。那个被查出来的字符串会被附在系统传递给你的搜索activity的intent(这个intent使用你之前为建议项定义的action)中。要查看这个字符串,使用getStringExtra(SearchManager.ACTION_MSG)。如果选中的建议项对应的数据不存在,那么这个action key将会被忽略。

示例:

保存为res/xml/searchable.xml的XML文件,如代码清单9-22所示:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/search_label"
    android:hint="@string/search_hint"
    android:searchSuggestAuthority="dictionary"
    android:searchSuggestIntentAction="android.intent.action.VIEW"
    android:includeInGlobalSearch="true"
    android:searchSettingsDescription="@string/settings_description" >
</searchable>

 

代码清单9-22

 本文来自jy02432443,是本人辛辛苦苦一个个字码出来的,转载请保留出处,并保留追究法律责任的权利 QQ78117253 

posted @ 2013-12-19 16:09  jy02432443  阅读(2177)  评论(0编辑  收藏  举报