编者注
这篇文章是我们在CodeProject赞助商。这些文章旨在为您提供的产品和我们认为服务的信息,有用的开发价值和
英特尔开发人员专区提供了工具,以及如何对跨平台应用程序开发,平台和技术信息,代码示例和对专业知识的信息,以帮助开发者进行创新并取得成功。加入我们的社区的Android,物联网,英特尔RealSense™技术,并视窗下载工具,获取开发套件,分享创意与志同道合的开发者,并参与编程马拉松的,竞赛,路演,和当地活动。
搜索是存在于大量的Android应用程序的功能。提供了一种方法来搜索您的应用程序中的内容是非常重要的,因为它可以帮助用户找到他们想要的内容。然而,这个搜索必须快速,高效地获取信息可能是用户打开您的应用程序的主要原因。Android SDK提供了一组API实现在应用这种模式,并在此文章中,我们将回顾落实到你的应用程序所需的第一步。
在手!
让我们开始吧!创建使用“空活动”模板的Android Studio中的一个新项目。一旦项目被创建,添加新的菜单文件,并将它命名为RES /菜单/ menu_search.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/search"
android:title="@string/hint_search"
android:icon="@android:drawable/ic_menu_search"
app:showAsAction="collapseActionView|ifRoom"
app:actionViewClass="android.support.v7.widget.SearchView" />
</menu>
我们的菜单文件只包含一个项目负责显示搜索按钮。当用户按下该按钮时,它膨胀并示出了要搜索的文本字段允许使用输入术语。负责这项任务的小部件是搜索查看
,因为我们可以在看到应用程序:actionViewClass
属性。定义应用程序:showAsAction
与价值collapseActionView
允许搜索查看
当按钮被窃听扩大自身。
请注意,我们使用的是支持库在这里保持在Android版本的兼容性。
一旦我们创建了一个菜单文件,我们将其加载到MainActivity
像描述如下:

public class MainActivity extends AppCompatActivity
implements SearchView.OnQueryTextListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_search, menu);
MenuItem searchItem = menu.findItem(R.id.search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setOnQueryTextListener(this);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
// User pressed the search button
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
// User changed the text
return false;
}
}
内部onCreateOptionsMenu()
我们使用加载菜单资源文件膨胀()
。通过菜单
对象,我们发现该菜单项
包含搜索查看
小部件。一个设置OnQueryTextListener
对象到搜索查看
我们的应用程序能够检测两个事件:
-
onQueryTextChange
被称为当用户在文本字段中的每个字符; -
onQueryTextSubmit
按下搜索时被触发。
所以,你可以实现任何你在这些方法中要执行搜索(访问SQLite数据库或Web服务,例如)。而已!这是为了实现你的应用程序搜索功能,最简单的方法。
改善搜索体验
以前的方法工作得很好,但一般需要一个搜索操作,以帮助用户找到他们想要的东西。要做到这一点的一个好方法是显示在用户输入一些建议。为了证明这种方法,我们将展示城市的名单,而用户键入全市名称搜索查看
。这个列表被存储在服务器中。我们要做的第一件事情就是创建一个可搜索的配置。于是,一个新的文件添加到项目RES / XML / searchable.xml
:
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="@string/hint_search"
android:label="@string/app_name"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
android:searchSuggestAuthority="ngvl.android.demosearch.citysuggestion"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSuggestIntentData="content://ngvl.android.demosearch.city"/>
让我们来了解这个文件中定义的属性:
-
语音搜索键,使用启用
voiceSearchMode:Android的
属性。 -
当用户键入的
查询()
方法将在响应内容提供商被称为安卓searchSuggestAuthority
(我们后来才看到这个供应商)。 -
当用户选择列表中的一个建议,一个新活动将使用中所描述的动作被称为
searchSuggestIntentAction:机器人
属性。 -
机器人:searchSuggestIntentData
,因为它定义了一个补充了先前的财产乌里
当用户点击一个建议的意图模式触发。
正如我们所知,内容提供商通常访问SQLite数据库来存储和检索数据,但我们可以用它来访问网络也是如此,Android文档清清楚楚地写着:
内容提供商“可以从多个线程同时被调用,并且必须是线程安全的。”
在本实施例中,建议将从存储在web服务器JSON文件进行检索。创建一个新的内容提供商将其命名为CitySuggestionProvider
,只是执行查询()
(其他都是空的):

public class CitySuggestionProvider extends ContentProvider {
List<String> cities;
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
if (cities == null || cities.isEmpty()){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://dl.dropboxusercontent.com/u/6802536/cidades.json")
.build();
try {
Response response = client.newCall(request).execute();
String jsonString = response.body().string();
JSONArray jsonArray = new JSONArray(jsonString);
cities = new ArrayList<>();
int lenght = jsonArray.length();
for (int i = 0; i < lenght; i++) {
String city = jsonArray.getString(i);
cities.add(city);
}
} catch (Exception e) {
e.printStackTrace();
}
}
MatrixCursor cursor = new MatrixCursor(
new String[] {
BaseColumns._ID,
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
}
);
if (cities != null) {
String query = uri.getLastPathSegment().toUpperCase();
int limit = Integer.parseInt(uri.getQueryParameter(SearchManager.SUGGEST_PARAMETER_LIMIT));
int lenght = cities.size();
for (int i = 0; i < lenght && cursor.getCount() < limit; i++) {
String city = cities.get(i);
if (city.toUpperCase().contains(query)){
cursor.addRow(new Object[]{ i, city, i });
}
}
}
return cursor;
}
//
Other methods are empty...
}
当用户开始键入的文本,我们的供应商是使用URI象下面这样调用。内容://ngvl.android.demosearch.citysuggestion/search_suggest_query/santos上限= 50
系统调用我们的供应商加入search_suggest_query / <文本>
中的URI的结尾。结果的上限设置为50默认情况下,它作为查询参数传递。值得注意的是,搜索操作UI线程之外完成所以像我们这样做,我们可以执行HTTP请求是很重要的。在这个例子中,我们存储了全市内存列表,但在实践中,我们可以在我们的SQLite数据库使用的表。
使用开放的
对象,我们已经得到了使用文本参数getLastPathSegment()
的建议,使用退回,最大数getQueryParameter()
。阅读从网络内容后,MatrixCursor建代表建议的城市名单。这里一个重要的细节是列名。列“_id”和“suggest_text_1”是强制性的。您可以使用更多的列或创建光标适配器的子类来定制您的建议名单。
正如我之前说的,我们的建议是存储在Web服务器上的JSON文件。所以在这里,我们正在执行的请求来检索城市名单和阅读它作为使用OkHttp库中的JSON文件。所以,不要忘记添加了这种依赖的build.gradle
文件:
dependencies { ... compile 'com.squareup.okhttp3:okhttp:3.0.1' }
一定要添加INTERNET权限在AndroidManifest.xml中
,内容提供商声明如下图所示:
<manifest ...
<uses-permission android:name="android.permission.INTERNET"/>
<application...
<provider
android:name=".CitySuggestionProvider"
android:authorities="ngvl.android.demosearch.citysuggestion"
android:enabled="true"
android:exported="true"/>
可以肯定的安卓当局
是因为搜索的配置文件中声明的一样。
之后,我们还需要将活动与搜索的连接配置。更改活动声明的AndroidManifest.xml
:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable"/>
</activity>
使用值android.app.searchable
为<元数据>
标签,我们正在定义的活动有一个可搜索的配置。现在,我们已将此信息提供给搜索查看
部件。修改onCreateOptionsMenu()
看起来像如下:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_search, menu);
MenuItem searchItem = menu.findItem(R.id.search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setOnQueryTextListener(this);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView.setSearchableInfo(searchManager.getSearchableInfo(
new ComponentName(this, MainActivity.class)));
searchView.setIconifiedByDefault(false);
return true;
}
运行应用程序并开始输入搜索查看里面的一些文字。你会看到,一些建议显示:
太好了!我们的建议功能工作正常。但是,当选择了一个建议,会发生什么?或者,如果我按在键盘搜索按钮?
处理建议和搜索行动
在这个时刻,当用户按下搜索按钮或选择列表中的一个建议,创建并显示在MainActivity的新实例。为了避免这种情况,我们可以设置我们的活动的启动模式singleTop
在AndroidManifest.xml中
贯彻onNewIntentMethod()
:
<activity android:name=".MainActivity"
android:launchMode="singleTop">
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Toast.makeText(this, "Searching by: "+ query, Toast.LENGTH_SHORT).show();
} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
String uri = intent.getDataString();
Toast.makeText(this, "Suggestion: "+ uri, Toast.LENGTH_SHORT).show();
}
}
当用户点击搜索按钮,该活动是使用所谓的ACTION_SEARCH,但在选择时建议,使用ACTION_VIEW(如我们在搜索的配置文件中定义)。如果你现在运行应用程序并执行搜索,同样的实例MainActivity
使用,而不是创建一个新的。
您可以使用这种方法没有问题,但你可以需要(或喜欢)来执行的其他活动进行搜索。为了做到这一点,我们要创建一个新的搜索活动,当用户按下搜索按钮或选择一个建议,将被调用。
它在声明中AndroidManifest.xml中
必须是这样的:
<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>
记得删除这些相同的配置(<意向过滤器>
和<元数据>
)从MainActivity
声明。所以,现在,我们必须执行的变化不大MainActivity
。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
...
searchView.setSearchableInfo(searchManager.getSearchableInfo(
new ComponentName(this, SearchableActivity.class)));
...
}
// Remove onNewIntent method too
与此实现中,当用户按下返回/搜索键,一个新活动使用操作“开始android.intent.action.SEARCH
”和一个参数称为“ 查询
”。该参数表示由用户键入的文本搜索查看
。下面的代码显示如何处理在搜索SearchableActivity
:
public class SearchableActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_searchable);
TextView txt = (TextView)findViewById(R.id.textView);
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
txt.setText("Searching by: "+ query);
} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
String uri = intent.getDataString();
txt.setText("Suggestion: "+ uri);
}
}
}
一旦你有一个查询参数可以搜索任何你想要的。例如,一个本地数据库/内容提供者或Web服务。