添加近期查询建议

使用 Android 搜索对话框或搜索微件时,您可以根据近期的搜索查询来提供搜索建议。例如,如果用户先前搜索过“小狗”,那么一旦用户开始输入相同的查询,该查询就会显示为一条建议。图 1 显示了包含近期查询建议的搜索对话框的示例。

开始之前,您需要实现搜索对话框或搜索微件,以便在您的应用中进行基本搜索。如果您还没有实现,请参阅创建搜索界面

基础知识

图 1. 包含近期查询建议的搜索对话框的屏幕截图。

近期查询建议只不过是保存的搜索。当用户选择某条建议时,可搜索 Activity 会接收一个 ACTION_SEARCH intent,它将该建议作为搜索查询,而可搜索 Activity 已处理过该查询(如创建搜索界面中所述)。

要提供近期查询建议,您需要执行以下操作:

  • 实现一个可搜索 Activity,如创建搜索界面中所述。
  • 创建一个扩展 SearchRecentSuggestionsProvider 的内容提供程序,并在应用清单中声明该提供程序。
  • 使用有关用于提供搜索建议的内容提供程序的信息来修改可搜索配置。
  • 每次执行搜索时,都将查询保存到内容提供程序。

就像 Android 系统显示搜索对话框一样,它也会在搜索对话框或搜索微件下方显示搜索建议。您只需提供一个来源,系统可从其检索建议。

如果系统发现您的 Activity 可搜索并提供搜索建议,一旦用户开始输入查询,就会发生以下过程:

  1. 系统获取搜索查询文本(用户到目前为止已输入的所有内容),并对包含建议的内容提供程序执行查询。
  2. 内容提供程序返回一个 Cursor,它指向与搜索查询文本匹配的所有建议。
  3. 系统显示由该 Cursor 提供的建议的列表。

显示近期查询建议后,可能会发生以下情况:

  • 如果用户输入其他键或以任何方式更改查询,系统会重复执行上述步骤,并更新建议列表。
  • 如果用户执行搜索,系统会忽略建议,并使用正常的 ACTION_SEARCH intent 将搜索传递给可搜索 Activity。
  • 如果用户选择某条建议,系统会使用建议的文本作为查询,将一个 ACTION_SEARCH intent 传递给可搜索 Activity。

您为内容提供程序扩展的 SearchRecentSuggestionsProvider 类会自动完成上述工作,因此实际上需要编写的代码非常少。

创建内容提供程序

近期查询建议所需的内容提供程序必须是 SearchRecentSuggestionsProvider 的实现。此类几乎能为您做一切。您只需编写用来执行一行代码的类构造函数即可。

例如,下面是近期查询建议的内容提供程序的完整实现:

Kotlin

    class MySuggestionProvider : SearchRecentSuggestionsProvider() {
        init {
            setupSuggestions(AUTHORITY, MODE)
        }

        companion object {
            const val AUTHORITY = "com.example.MySuggestionProvider"
            const val MODE: Int = SearchRecentSuggestionsProvider.DATABASE_MODE_QUERIES
        }
    }
    

Java

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

setupSuggestions() 的调用会传递搜索授权方的名称和一种数据库模式。搜索授权方可以是任何唯一的字符串,但最佳做法是使用内容提供程序的完全限定名称(软件包名称后跟提供程序的类名;例如“com.example.MySuggestionProvider”)。数据库模式必须包含 DATABASE_MODE_QUERIES,并且可以选择性地包含 DATABASE_MODE_2LINES,它会向建议表格中再添加一列,可让您为每条建议提供第二行文本。例如,如果您要在每条建议中提供两行,请编写以下代码:

Kotlin

    const val MODE: Int = DATABASE_MODE_QUERIES or DATABASE_MODE_2LINES
    

Java

    public final static int MODE = DATABASE_MODE_QUERIES | DATABASE_MODE_2LINES;
    

现在,在应用清单中声明内容提供程序,使用的授权方字符串与在 SearchRecentSuggestionsProvider 类中(以及在可搜索配置中)使用的字符串相同。例如:

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

修改可搜索配置

要配置系统以使用建议提供程序,您需要在可搜索配置文件中将 android:searchSuggestAuthorityandroid:searchSuggestSelection 属性添加到 <searchable> 元素。例如:

    <?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>
    

android:searchSuggestAuthority 的值应该是内容提供程序的完全限定名称,与内容提供程序中使用的授权方(上例中的 AUTHORITY 字符串)完全匹配。

android:searchSuggestSelection 的值必须是一个问号,前面加上一个空格 (" ?"),它是 SQLite 选择参数的占位符(会自动由用户输入的查询文本替换)。

保存查询

要填充近期查询的集合,请将可搜索 Activity 接收的每个查询添加到 SearchRecentSuggestionsProvider。为此,请创建 SearchRecentSuggestions 的实例,并且每次可搜索 Activity 接收查询时都调用 saveRecentQuery()。例如,以下示例展示了您如何在 Activity 的 onCreate() 方法执行期间保存查询:

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        if (Intent.ACTION_SEARCH == intent.action) {
            intent.getStringExtra(SearchManager.QUERY)?.also { query ->
                SearchRecentSuggestions(this, MySuggestionProvider.AUTHORITY, MySuggestionProvider.MODE)
                        .saveRecentQuery(query, null)
            }
        }
    }
    

Java

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

SearchRecentSuggestionsProvider 构造函数需要的授权方和数据库模式与内容提供程序声明的相同。

saveRecentQuery() 方法将搜索查询字符串当作第一个参数,并且选择性地包含第二个字符串作为建议的第二行(或为 null)。仅当您通过 DATABASE_MODE_2LINES 为搜索建议启用了两行模式时,才会使用第二个参数。如果您启用了两行模式,则当系统查找匹配的建议时,查询文本也会与第二行匹配。

清除建议数据

为了保护用户的隐私,您应始终为用户提供清除近期查询建议的方法。要清除查询记录,请调用 clearHistory()。例如:

Kotlin

    SearchRecentSuggestions(this, HelloSuggestionsProvider.AUTHORITY, HelloSuggestionsProvider.MODE)
            .clearHistory()
    

Java

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

通过由您选择的“清除搜索记录”菜单项、偏好设置项或按钮来执行此操作。您还应提供一个确认对话框来确认用户想要删除他们的搜索记录。