Android搜索框以及内容提供器
先看结果:

相关的官方文档在这里:Creating a Search Interface
Android官方提供了两种方式:
- 弹出一个Dialog,覆盖当前的Activity界面 
- 在AppBar中扩展一个搜索框。 - 这个是上面动态图片展示的方式。以下介绍的是这种方式的实现。 
 官方建议:如果你写的程序是给Android 3.0 以上的设备使用,那么推荐使用AppBar的方式。
想要完成这个功能,你需要创建以下几个文件:
- 一个XML文件,用于配置搜索框。该文件路径:res/xml/searchable.xml - 该文件会被系统用来创建SearchableInfo对象 
- 一个用于接收搜索关键词并展示最终结果的Activity 
- 一个内容提供器,用于提供搜索建议 
分为两部分写。先完成搜索功能,再添加提供搜索建议的功能。
第一部分:基本的搜索功能
这个部分完成五个文件的创建或修改:
- MainActivity.java
 配置AppBar
- SearchableActivity.java
 根据Intent的Action,显示intent的内容
- res/xml/searchable.xml
 配置搜索框
- res/menu/options_menu.xml
 添加搜索框及配置AppBar
- AndroidManifest.xml
 配置SearchableActivity,使其接收ACTION_SEARCH的Intent
该版本的完整代码:SearchWidgetInAppBar - 完成基本的功能
searchable.xml
初始的xml:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_name"
    android:hint="@string/search_hint"
    >
</searchable>
之后添加“搜索建议”功能的时候,还需要对它进行修改。
展示结果的Activity
当用户执行一个搜索的时候,系统会启动该Activity,并且传入搜索的词汇。这个词汇包含在Intent中,并且标记为ACTION_SEARCH动作。
现在创建一个简单地包含TextView的Activity就行了。这里将其命名为 SearchableActivity。
打开AndroidManifest.xml对该Activity进行配置:
<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>
由于intent-filter的设置,当接收到标记为ACTION_SEARCH的动作时,会启动该Activity。
SearchableActivity.java
public class SearchableActivity extends AppCompatActivity {
    TextView mTvWord = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_searchable);
        mTvWord = (TextView) findViewById(R.id.tv_word);
        Intent intent = getIntent();
        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra(SearchManager.QUERY);
            String text = getString(R.string.notice) + query;
            mTvWord.setText(text);
        }
    }
}
作为示例,只展示要查询的单词是什么就可以了。
为了让其他Activity可以打开该Activity,在AndroidManifest.xml继续设置:
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    <application ...>
        ...
        <meta-data
            android:name="android.app.default_searchable"
            android:value=".SearchableActivity"/>
    </application>
</manifest>
AppBar的设置
添加一个搜索按钮。
res/menu/options_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_search_white_24dp"
        android:title="@string/action_search"
        app:showAsAction="ifRoom|collapseActionView"
        app:actionViewClass="android.support.v7.widget.SearchView"/>
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        app:showAsAction="never"/>
</menu>
设置的图标可以到Material icons下载。将解压后Android文件夹里面的所有文件复制到res/文件夹底下就行了。
app:actionViewClass="android.support.v7.widget.SearchView"
如果不设置这项,会导致错误。下面会提到。
collapseActionView是为了可以展开搜索框。
MainActivity.java
public class MainActivity extends AppCompatActivity {
    ...
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.options_menu, menu);
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setIconifiedByDefault(false);
        searchView.setSubmitButtonEnabled(true);    // 显示“开始搜索”的按钮
        searchView.setQueryRefinementEnabled(true); // 提示内容右边提供一个将提示内容放到搜索框的按钮
        return true;
    }
}
到目前为止的效果

该版本的完整代码:SearchWidgetInAppBar - 完成基本的功能
第二部分:添加搜索建议
官方文档:Adding Custom Suggestions
这一部分需要做的是:
- 添加一个内容提供器(ContentProvider),为搜索建议框提供数据
- 一张SQLite表,用于给内容提供器查询
- 修改searchable.xml文件,添加搜索建议的支持
该版本的完整代码:SearchWidgetInAppBar - 完成搜索建议
数据库
这里用ORMLite作为例子。如果想用Android自带数据库,可以查看官方例子:SearchableDictionary
数据表:
@DatabaseTable(tableName = "tb_def")
public class Word {
    @DatabaseField(generatedId = true, columnName = COLUMN_ID)
    private int id;
    @DatabaseField(columnName = COLUMN_WORD)
    private String word;
    @DatabaseField(columnName = COLUMN_SUGGESTION)
    private String suggestion;
    public static final String COLUMN_ID = BaseColumns._ID;
    public static final String COLUMN_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;
    public static final String COLUMN_SUGGESTION = SearchManager.SUGGEST_COLUMN_INTENT_DATA;
    ...
    public Word(int id, String word, String suggestion) {
    this.id = id;
    this.word = word;
    this.suggestion = suggestion;
    }
    ...
}
这里的id字段设置为BaseColumns._ID是为了让ListView可以读取。搜索建议是显示在ListView上的。
word字段设置为SearchManager.SUGGEST_COLUMN_TEXT_1是将该字段作为建议显示的文本。如果每个建议想显示两行数据,还有SearchManager.SUGGEST_COLUMN_TEXT_2。更多内容可以见:SuggestionTable
除此之外,还有一个字段suggestion。当你点击搜索建议中的数据时,系统会将该字段的数据放入Intent传送给SearchableActivity。
数据库:
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
    ...
    public Cursor getSuggestionWords(String word) {
        QueryBuilder<Word, Integer> qb = getWordDao().queryBuilder();
        CloseableIterator<Word> iterator = null;
        try {
            qb.distinct().where().like(Word.COLUMN_WORD, word + "%");
            iterator = getWordDao().iterator(qb.prepare());
            AndroidDatabaseResults results = (AndroidDatabaseResults) iterator.getRawResults();
            return results.getRawCursor();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (iterator != null) {
                iterator.closeQuietly();
            }
        }
        return null;
    }
    ...
}
由于ContentProvider需要Cursor作为结果,因此这里用了ORMLite作者所说的方法:Android Cursor with ORMLite to use in CursorAdapter
创建内容提供器
public class DictionaryProvider extends ContentProvider {
    public static String AUTHORITY = "com.schaepher.memorywarehouse.DictionaryProvider";
    private DatabaseHelper mDatabaseHelper = null;
    private static final int SEARCH_SUGGEST = 0;
    private static final UriMatcher mURIMatcher = buildUriMatcher();
    private static UriMatcher buildUriMatcher() {
        UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
        return matcher;
    }
    @Override
    public boolean onCreate() {
        mDatabaseHelper = DatabaseHelper.getHelper(getContext());
        return false;
    }
    @Override
    public Cursor query(@NonNull Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        String query = uri.getLastPathSegment();
        int i = mURIMatcher.match(uri);
        if (i == SEARCH_SUGGEST) {
            return mDatabaseHelper.getSuggestionWords(query);
        } else {
            throw new IllegalArgumentException("Unknown Uri: " + uri);
        }
    }
    @Override
    public String getType(@NonNull Uri uri) {
        int i = mURIMatcher.match(uri);
        if (i == SEARCH_SUGGEST) {
            return SearchManager.SUGGEST_MIME_TYPE;
        } else {
            throw new IllegalArgumentException("Unknown URL " + uri);
        }
    }
    ...
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.ftd.schaepher.memorywarehouse"
          xmlns:android="http://schemas.android.com/apk/res/android">
    <application ...>
        ...
        <provider
            android:name=".DictionaryProvider"
            android:authorities="com.schaepher.memorywarehouse.DictionaryProvider"
            android:enabled="true"
            android:exported="false">
        </provider>
    </application>
</manifest>
searchable.xml
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
            android:label="@string/app_name"
            android:hint="@string/search_hint"
            android:searchSuggestAuthority="com.schaepher.memorywarehouse.DictionaryProvider"
            android:searchSuggestIntentAction="android.intent.action.VIEW">
</searchable>
当点击搜索建议时,传入Intent的Action是ACTION_VIEW。
SearchableActivity
SearchableActivity.java
public class SearchableActivity extends AppCompatActivity {
    TextView mTvWord = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_searchable);
        mTvWord = (TextView) findViewById(R.id.tv_word);
        Intent intent = getIntent();
        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            mTvWord.append(intent.getStringExtra(SearchManager.QUERY));
        } else if (Intent.ACTION_VIEW.equals((intent.getAction()))){
            mTvWord.append(intent.getDataString());
        } else {
            mTvWord.setText(R.string.word_not_found);
        }
    }
}
到目前为止的效果

该版本的完整代码:SearchWidgetInAppBar - 完成搜索建议
Android搜索框以及内容提供器的更多相关文章
- Android入门(十四)内容提供器-实现跨程序共享实例
		原文链接:http://www.orlion.ga/661/ 打开SQLite博文中创建的 DatabaseDemo项目,首先将 MyDatabaseHelper中使用 Toast弹出创建数据库成功的 ... 
- android: 创建自己的内容提供器
		我们学习了如何在自己的程序中访问其他应用程序的数据.总体来说思 路还是非常简单的,只需要获取到该应用程序的内容 URI,然后借助 ContentResolver 进行CRUD 操作就可以了.可是你有没 ... 
- <Android基础> (七)内容提供器
		第七章 内容提供器 7.1 内容提供器(Content Provider) 主要应用于在不同的应用程序之间实现数据共享功能.允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性. 7.2 ... 
- android学习十二(android的Content Provider(内容提供器)的使用)
		文件存储和SharePreference存储以及数据存储一般为了安全,最好用于当前应用程序中訪问和存储数据.内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能 ... 
- android笔记 : Content provider内容提供器
		内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能. 内容提供器的用法一般有两种,一种是使用现有的内容提供器来读取和操作相应程序中的数据,另一种是创建自己的内 ... 
- Android入门(十三)内容提供器
		原文链接:http://www.orlion.ga/612/ 内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一 ... 
- Android学习笔记(二十)——自定义内容提供器
		//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 如果我们想要实现跨程序共享数据的功能,官方推荐的方式就是使用内容提供器,可以通过新建一个类去继承 Conten ... 
- Android学习笔记(十九)——内容提供器
		//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整 ... 
- Android 创建内容提供器(ContentResolver)
		如果想实现跨程序共享数据的功能,官方推荐的方式就是使用内容提供器,可以通过新建一个类去继承 ContentResolver 的方式来创建一个自己的内容提供器. ContentProvider 类中有六 ... 
随机推荐
- 在Action类中获得HttpServletResponse对象的四种方法
			在struts1.xAction类的execute方法中,有四个参数,其中两个就是response和request.而在Struts2中,并没有任何参数,因此,就不能简单地从execute方法获得Ht ... 
- php字符串压缩
			在PHP中偶尔遇到字符串的压缩,比如一个长字符串,数据库开始设计的字段存不下,但是又不想改数据库字段存储长度,就可以用压缩的方式降低数据字段字符串的长度数量级,把几百个字符的字符串压缩到几十个字符.总 ... 
- iOS纯代码制作欢迎界面——UIScrollView, UIPageControl, UIImageView,UIButton, NSTimer
			欢迎界面,还是比较简单的,一个UIScrollView控件,一个UIPageControl,几个UIImageView即可摆平.在这里光玩这些,就显得诚意不足了.特意拓展一下,再加几个UIButton ... 
- sqlserver 脚本方式导出数据到excel
			use EntDataCenter go SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- =========================== ... 
- 微信小程序之----组件
			1.view 把文档分割为独立的.不同的部分. view组件类似于html中的div标签,默认为块级元素,独占一行,可以通过设置display:inline-block改为行级元素. view.wxm ... 
- 中国产品众筹NO.1诞生
			中国产品众筹NO.1诞生 淘宝众筹打响新拐点之战 http://bbs.taobao.com/catalog/thread/508895-317240623.htm?spm=1.7274553.199 ... 
- IOS开发中UIAlertController(警告框)的使用
			步骤一.初始化: UIAlertController * inputname = [UIAlertController alertControllerWithTitle:@"未输入账户&qu ... 
- java系列--HTTP协议
			一.HTTP请求信息 请求行 请求头 空行 消息体 1.防盗链: 枚举类型: 二.中文乱码问题 1.Get提交 String username = request.getParameter(" ... 
- linux mint运行docker
			1,sudo apt-get install docker.io 或者sudo apt-get install docker* 2,安装好之后 sudo docker -d 启动进程提示: yimiy ... 
- 12.TCP的成块数据流
			1.滑动窗口协议 TCP滑动窗口的可视化表示 我们将字节从1到11进行标号,接收方通告的窗口称为提供的窗口,它覆盖了第4字节到第9字节的数据,且通告窗口大小为6.发 ... 
