13.3.2 为应用定制搜索功能

为自己的应用定制搜索功能,需要实现并配置搜索结果界面组件,描述搜索对话框的样式,并可以依照需要选择性地存储搜索历史。

搜索结果界面组件的实现,与一般的界面组件并无太大差异,只需要依照搜索意图的限定进行处理,其常用的编程模式如下:


@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);

}

}

除此之外,就需要有相应的组件配置信息,放置在配置文件AndroidManifest.xml中:

<application…>

<activity android:name=".MySearchableActivity"

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>

</application>


通常而言,我们会将其任务模式android:launchMode配置成singleTop,使它更接近于作为搜索结果组件表现出来的特征[1]

除此之外,在搜索结果组件中还有一个<meta-data>域。它并不是用于描述搜索结果组件的,而是用于描述该搜索结果组件对应的搜索对话框的。一个示例如下:


<?xml version="1.0"encoding="utf-8"?>

<searchable xmlns:android=

"http://schemas.android.com/apk/res/android"

android:label="好友搜索"

android:hint="输入需要搜索的好友名字">

</searchable>


其中,android:label是该搜索组件的名字,而android:hint是搜索框中的提示信息,除此之外,在该资源文件中还可以配置搜索按钮、是否支持语音搜索等[2]

搜索结果界面组件设置完成后,并不能接受搜索请求开始工作,开发者还需要将其配置到应用或需要搜索功能的界面组件中。搜索界面组件的部署,也是通过<meta-data>,键android:name为android.app.default_searchable,值android:value为搜索结果的界面组件:


<application…>

<!—可以为界面组件指定其搜索组件—>

<activity android:name=".UserActivity">

<meta-data android:name="android.app.default_searchable"

android:value=".UserSearchActivity"/>

</activity>

<!—可以为整个应用指定其搜索组件—>

<meta-data android:name="android.app.default_searchable"

android:value=".OtherSearchActivity"/>

</application>


把搜索组件配置到<application>域中,该搜索组件会在整个应用的搜索界面中生效,在应用的任何一个界面组件中单击搜索键都可以弹出它的搜索对话框;如果只是配置在<activity>域中,就只可以在该界面组件中才能够弹出对应的搜索对话框。如果同时在<application>域和<activity>域配置了搜索组件,<activity>域中的搜索组件就会覆盖<application>域的搜索组件而生效。

如果界面组件需要对其中的搜索组件进行控制,还可以在代码中进行设置。界面组件可以重载Activity.onSearchRequested来附加额外的数据或者是改变搜索逻辑。比如,为搜索结果组件附加额外的数据,可以将这些数据打包成一个android.os.Bundle对象,放置到SearchManager.APP_DATA附加域中:


@Override

public boolean onSearchRequested(){

//创建附加的数据

Bundle extraData=new Bundle();

extraData.putBoolean("just_friend",true);

//发送搜索请求,附加额外的数据

startSearch(null, false, extraData, false);

return true;

}

搜索结果组件需要额外支持对应的附加数据,比如:

Bundle extraData=getIntent().getBundleExtra(

SearchManager.APP_DATA);

if(appData!=null){

boolean justFriend=

extraData.getBoolean("just_friend");

}


如果应用需要存储搜索历史,提供搜索建议,在用户键入搜索词后自动提示曾经输入过的相关搜索词,那么就需要用到Android的搜索历史功能。实现搜索历史功能,需要定制一个特殊的数据源组件,它派生自android.content.SearchRecentSuggestionsProvider类,是ContentProvider的子类,在该数据源组件中,会构造数据库来存储搜索历史数据。

与所有数据源组件一样,搜索历史数据源也需要有一个Uri作为其唯一的身份标识,并在配置文件中声明:


<application>

<provider android:name=".SuggestionProvider"

android:authorities="com.test.SuggestionProvider"/>

</application>


搜索历史数据源的实现非常简单,只需要派生SearchRecentSuggestionsProvider,并调用SearchRecentSuggestionsProvider.setupSuggestions函数即可:


public class MySuggestionProvider extends

SearchRecentSuggestionsProvider{

public MySuggestionProvider(){

setupSuggestions("com.test.SuggestionProvider",

DATABASE_MODE_QUERIES);

}

}


有了数据源组件,便需要往其中填充数据才能帮助检索。插入搜索历史记录的最好时机,是当搜索词传入到搜索结果组件中时,此时,可以在Activity.onCreate函数中添加搜索历史:


public void onCreate(Bundle savedInstanceState){

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);

}

}


如果开发者希望在搜索建议中出现的不是历史记录,而是预设的一些搜索建议,可以依照标准构造数据源组件,来取代原有的搜索历史数据源组件[3]

[1]搜索结果组件的任务模型,并没有任何强制的限定,开发者可以依据它的场景进行配置。

[2]更多搜索框的配置信息,可以参考SDK文档:http://developer.android.com/guide/topics/search/searchable-config.html。

[3]关于自定义搜索建议数据源的介绍,请参见SDK:http://develper.android.com/guide/topics/search/adding-custom-suggestions.html。