木鸟杂记

分布式系统,数据库,存储

Android 学习笔记(一):搜索框的实现

目标

输入关键字,实时显示搜索结果。

作者:木鸟杂记 https://www.qtmuniao.com, 转载请注明出处

过程

官方文档入手,由于初入门,相关术语懂得少,而该文档又非代码级实现,导致没能完整搭起搜索的架子。该文档主要讲了以下几点:

  1. 两种实现方式,search dialog和search widget;对于Android 3.0 以后的机器,推荐使用后者,较为灵活。
  2. 三个主要组件:
    • 搜索配置(a searchable configuration)
    • 搜索容器(a searchable Activity)(困惑点1)
    • 搜索结构(a search interface)
  3. 搜索过程:
    • 接受查询(Receive the query)
      使用Itent(困惑点2)
    • 查询数据(Search your data)
    • 呈现结果(Present the results)(困惑点3)

我想使用Search Widget方式,主要遇到以下几个困惑点:

  1. 如何在Activity中触发搜索,就是AppBar右上角的搜索图标如何做出来。
  2. 如果使用Intent的查询数据,如示例一般,应该不能做到实时匹配输入字符。我猜想应该有listener之类的,但是例子没给。
  3. 如何呈现结果,文档建议让SearchAbleActivity继承ListView来实现,但是具体细节,如怎么接受结果,传递给ListView,都没有提。
    等于搜索过程的三个环节都没有搞清楚,一脸懵逼。

于是搜索关键词 SearchView action bar,找到一篇帖子:Implementing SearchView in action bar,反复琢磨,才弄清楚了以上几个问题。

首先,对于触发搜索,该回答使用的是具有App Bar的Activity作为SearchableActivity,并且在复写onCreateOptionsMenu函数,实例化其参数menu,并且将SearchView作为其一个item。如此一来,SearchableActivity的右上角就会有搜索按钮。相关代码如下:
res\menu\search.xml:

1
2
3
4
5
6
7
8
9
<?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/search_menu"
android:title="@string/search_hint"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.widget.SearchView" />

</menu>

SearchableActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search, menu);
this.menu = menu;

// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.search_menu).getActionView();
// Assumes current activity is the searchable activity
ComponentName name = getComponentName();
searchView.setSearchableInfo(searchManager.getSearchableInfo(name));
searchView.setIconifiedByDefault(false);

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
doMySearch(s);
return false;
}

@Override
public boolean onQueryTextChange(String s) {
doMySearch(s);
return false;
}
});

return true;
}

AndroidManifest.xml

1
2
3
4
5
6
7
<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>

res/xml/searchable.xml

1
2
3
4
5
<?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/app_label" >
</searchable>

其次是实时匹配查询结果;也是在onCreateOptionsMenu函数中,给SearchView设置listeners,具体可以见上面代码,但是return true/false暂时有什么区别还没搞清楚。

最后是展示数据;如官方文档所说,利用ListView,具体做法是通过Adapter将数据(比如List)传给利用xml渲染(inflate)的ListView,该答案是用CursorAdaptor(对接数据库数据更合适)。具体代码如下:
res/layout/item.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/item"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>

ResultAdapter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ResultAdapter extends CursorAdapter {
private List<String> items;
private TextView text;

public ResultAdapter(Context context, Cursor cursor, List<String> items) {
super(context, cursor, false);
this.items = items;
}

@Override
public void bindView(View view, Context context, Cursor cursor) {
text.setText(items.get(cursor.getPosition()));
}

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.item, parent, false);
text = view.findViewById(R.id.item);
return view;
}
}

SearchableActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void doMySearch(String query){
String[] columns = new String[] { "_id", "text" };
Object[] temp = new Object[] { 0, "default" };

MatrixCursor cursor = new MatrixCursor(columns);
for(int i = 0; i < items.size(); i++) {

temp[0] = i;
temp[1] = items.get(i);
cursor.addRow(temp);

}

// SearchView
final SearchView search = (SearchView) menu.findItem(R.id.search_menu).getActionView();
search.setSuggestionsAdapter(new ResultAdapter(this, cursor, items));
}

参考资料:

  1. Android官方文档,https://developer.android.com/guide/topics/search/search-dialog.html
  2. Stack Overflow回答,https://stackoverflow.com/questions/21585326/implementing-searchview-in-action-bar
  3. 官方视频,https://www.youtube.com/watch?v=9OWmnYPX1uc

我是青藤木鸟,一个喜欢摄影、专注大规模数据系统的程序员,欢迎关注我的公众号:“木鸟杂记”,有更多的分布式系统、存储和数据库相关的文章,欢迎关注。 关注公众号后,回复“资料”可以获取我总结一份分布式数据库学习资料。 回复“优惠券”可以获取我的大规模数据系统付费专栏《系统日知录》的八折优惠券。

我们还有相关的分布式系统和数据库的群,可以添加我的微信号:qtmuniao,我拉你入群。加我时记得备注:“分布式系统群”。 另外,如果你不想加群,还有一个分布式系统和数据库的论坛(点这里),欢迎来玩耍。

wx-distributed-system-s.jpg