添加自定义搜索建议

您可以根据 Android 搜索对话框或搜索小部件中的最近搜索查询提供搜索建议。例如,如果用户查询“小狗”,则当他们再次键入相同的查询时,该查询将显示为建议。图 1 显示了一个带有最近查询建议的搜索对话框示例。

在开始之前,请在您的应用程序中为基本搜索实现搜索对话框或搜索小部件。要了解如何操作,请参阅 创建搜索界面

基础知识

图 1. 带有最近查询建议的搜索对话框的屏幕截图。

最近的查询建议是已保存的搜索。当用户选择建议时,您的可搜索活动将收到一个 ACTION_SEARCH 意图,其中建议作为您的可搜索活动已处理的搜索查询。

要提供最近的查询建议,您需要

  • 实现一个可搜索的活动。
  • 创建一个扩展 SearchRecentSuggestionsProvider 的内容提供程序,并在您的应用程序清单中声明它。
  • 使用提供搜索建议的内容提供程序的信息修改可搜索配置。
  • 每次执行搜索时将查询保存到您的内容提供程序。

就像 Android 系统显示搜索对话框一样,它会在对话框或搜索小部件下方显示搜索建议。您提供了系统从中检索建议的来源。

当系统识别出您的活动可搜索并提供搜索建议时,当用户键入查询时会发生以下情况

  1. 系统获取搜索查询文本(无论用户开始键入什么)并对包含您的建议的内容提供程序执行查询。
  2. 您的内容提供程序返回一个 Cursor,该游标指向与搜索查询文本匹配的所有建议。
  3. 系统显示由 Cursor 提供的建议列表。

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

  • 如果用户键入另一个键或以任何方式更改查询,则重复上述步骤并更新建议列表。
  • 如果用户执行搜索,则忽略建议,并使用普通的 ACTION_SEARCH 意图将搜索传递到您的可搜索活动。
  • 如果用户选择建议,则使用建议的文本作为查询将 ACTION_SEARCH 意图传递到您的可搜索活动。

您为内容提供程序扩展的 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 的值必须是内容提供程序的完全限定名称,该名称必须与内容提供程序中使用的权限完全匹配,例如前面示例中的 "com.example.MySuggestionProvider"

android:searchSuggestSelection 的值必须是在空格之前的一个问号:" ?"。这是 SQLite 选择参数的占位符,系统会自动将其替换为用户输入的查询文本。

保存查询

要填充您的最近查询集合,请将您的可搜索活动收到的每个查询添加到您的 SearchRecentSuggestionsProvider 中。为此,请创建 SearchRecentSuggestions 的实例,并在每次您的可搜索活动收到查询时调用 saveRecentQuery()。例如,以下是如何在活动的 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();

从您选择的“清除搜索历史记录”菜单项、首选项项或按钮中执行此操作。提供确认对话框以验证用户是否要删除其搜索历史记录。