Android为程序的搜索功能提供了统一的搜索接口,search dialog和search widget,这里简单介绍search dialog使用。search dialog 只能为于activity窗口的上方。下面以点击EditText输入框启动search dialog搜索框为例,效果如下:
该效果是在点击 EditText 时,自动跳转到 google 自带的搜索框界面。 google 自带的搜索框,其左边的图标为该 app 的图标。无论是点击该图标或者是该图标前面的箭头都是可以回到 EditText 所在的界面。
由于点击 EditText 会自动跳转界面,所以可以在跳转后的界面中自行布局,丰富界面。而本例是为了让其在当前界面实现跳转搜索,不需要额外的启动一个界面。所以在启动模式中使用“单顶模式”,即:singleTop 模式。一定要注意 google 自带的搜索框是位于 Activity 的顶部的(没办法修改)。
实现效果图中的大致步骤:
1. 在 res/xml目录下新建 searchable.xml 文件:
<span style="font-size:18px;"><?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></span>
注意:在新建 searchable.xml 时,带上后缀名。上面的代码是最简单的搜索配置代码,其中的 android:lable="@string/app_name" 必须要有,且 lable 的属性一定要为 @string/app_name。 hint 属性是提示的信息,不是必须的,这点和 EditText 类似。其他的属性可以参考 google 的官方文档。
2. 在 AndroidManifest.xml 中声明搜索的 Activity。
由于本例不需要在搜索时启动一个 Activity 所以,将其设置为 singleTop 模式。所以:
<span style="font-size:18px;"><meta-data
android:name="android.app.default_searchable"
android:value=".MainActivity"/></span>
在 activity 根标签内添加(因为启动模式为 singleTop 不会加载其他的界面,所以需要在 activity 标签内,如果需要在其他界面显示,则将其添加到注册该节目的 activity 根标签内):
<span style="font-size:18px;"><intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" /></span>
其中的 intent-filter 标签的 name 值必须为 android.intent.action.SEARCH,meta-data标签的 resource 值为 res/xml目录下的searchable.xml 文件;
<span style="font-size:18px;">@Override
public boolean onSearchRequested() {
//做其他的事
doOther();
return super.onSearchRequested();
}</span>
<span style="font-size:18px;"> public boolean onSearchRequested() {
Bundle data = new Bundle();
data.putString("key", "your info");
startSearch(null, true, data , false);
return true;
}</span>
<span style="font-size:18px;"> /**
* 由于 Activity 的启动模式为 singleTop ,搜索的 Action 必须为:
* android.intent.action.SEARCH,所以需要重写该方法,以便获取 Action 的“动作”。
* 如果不重写,Action 的返回值为 android.intent.action.MAIN
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 设置 intent 这样就能得到正确的 Action 了
setIntent(intent);
}
/**
* 该方法在 onNewIntent() 方法后执行,可以获取 onNewIntent() 方法设置的 intent,
* 也可以直接 onNewIntent() 方法进行得到 intent 等的操作
*/
@Override
protected void onResume() {
super.onResume();
Intent intent = getIntent();
if (intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
searchQurey(query, intent);
}
}</span>
<span style="font-size:18px;">package com.crazy.gemi.ui.cheaper;
import android.content.SearchRecentSuggestionsProvider;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.CancellationSignal;
import com.crazy.gemi.R;
/**
* 该类用于记录最近的搜索,并将其存入数据库
*/
public class SearchSuggestionSampleProvider
extends SearchRecentSuggestionsProvider {
public final static String AUTHORITY="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider";
public final static int MODE=DATABASE_MODE_QUERIES;
public SearchSuggestionSampleProvider(){
super();
setupSuggestions(AUTHORITY, MODE);
}
}
</span>
<span style="font-size:18px;"> <provider
android:authorities="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider"
android:name=".ui.cheaper.SearchSuggestionSampleProvider"/></span>
这样就完成了所有在 AndroidManifest.xml 中的工作,其中的完整代码如下:
<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.crazy.gemi">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/naryou_logo"
android:name=".ui.utils.MyApplication"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="android.app.default_searchable"
android:value=".MainActivity"/>
<activity android:name=".MainActivity" android:launchMode="singleTop">
<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>
<provider
android:authorities="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider"
android:name=".ui.cheaper.SearchSuggestionSampleProvider"/>
</application>
</manifest>
</span>
<span style="font-size:18px;">android:searchSuggestAuthority="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider"
android:searchSuggestSelection=" ? "</span>
<span style="font-size:18px;"><?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.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider"
android:searchSuggestSelection=" ? ">
</searchable></span>
<span style="font-size:18px;"> private void searchQurey(String query , Intent intent) {
//保存搜索记录
SearchRecentSuggestions suggestions=new SearchRecentSuggestions(this,
SearchSuggestionSampleProvider.AUTHORITY, SearchSuggestionSampleProvider.MODE);
suggestions.saveRecentQuery(query, null);
}</span>
<span style="font-size:18px;">package com.crazy.gemi.ui.cheaper;
import android.content.SearchRecentSuggestionsProvider;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.CancellationSignal;
import com.crazy.gemi.R;
/**
* 该类用于记录最近的搜索,并将其存入数据库
*/
public class SearchSuggestionSampleProvider
extends SearchRecentSuggestionsProvider {
public final static String AUTHORITY="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider";
public final static int MODE=DATABASE_MODE_QUERIES;
public SearchSuggestionSampleProvider(){
super();
setupSuggestions(AUTHORITY, MODE);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
String[] items = null;
Cursor cursor = super.query(uri, projection, selection, selectionArgs, sortOrder);
int arrayLength = cursor.getCount();
if (arrayLength != 0) {
items = new String[arrayLength + 1];
cursor.moveToFirst();
int i = 0;
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
int number = cursor.getColumnIndex("suggest_intent_query");
items[i] = cursor.getString(number);
i++;
}
// items[i] = "清空历史记录";
items[i] = getContext().getString(R.string.clear_search_keyword);
}else {
return null;
}
// suggest_format 等字段不能够改变
String[] columns = new String[]{"suggest_format", "suggest_text_1", "suggest_intent_query", "_id"};
// ContentProvider对外共享数据的时候,如果没有数据库,需要对外共享数据时则使用 MatrixCursor
MatrixCursor stringCursor = new MatrixCursor(columns);
String row[] = new String[4];
int i = 0;
for (CharSequence item : items) {
row[0] = "" + 0;
row[1] = item.toString();
row[2] = item.toString();
row[3] = "" + (++i);
stringCursor.addRow(row);
}
return stringCursor;
}
}
</span>
这样是不是就可以实现删除功能了呢?如果按照上面的步骤去执行,那就会看见当我们每次点击“删除历史记录”时,就会在搜索框中多出“删除历史记录”这个字眼。由此可以我们并没有实现该功能,只实现了添加“删除历史记录”字眼的功能。到这一步离实现“删除历史记录”只有一步之遥的距离了。执行在 searchQurey() 方法中添加如下代码即可:
<span style="font-size:18px;"> // 点击 “清空历史记录” 删除记录
if(query.equals(getString(R.string.clear_search_keyword))){
suggestions.clearHistory();
}else{
suggestions.saveRecentQuery(query, null);
}</span>
完整的 MainActivity.java 代码如下:
<span style="font-size:18px;">package com.crazy.gemi;
import android.app.SearchManager;
import android.content.Intent;
import android.graphics.Color;
import android.provider.SearchRecentSuggestions;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.widget.TextView;
import com.crazy.gemi.ui.cheaper.CheaperFragment;
import com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider;
import com.crazy.gemi.ui.favor.FavorFragment;
import com.crazy.gemi.ui.more.MoreFragment;
import com.crazy.gemi.ui.near.NearFragment;
import com.crazy.gemi.ui.pocket.PocketFragment;
public class MainActivity extends FragmentActivity
implements View.OnClickListener, CheaperFragment.SearchResult{
private TextView[] textView = new TextView[5];
private View[] views = new View[5];
// 其中的 firstFragment 相当于是个中间变量
private Fragment firstFragment, nearFragment, cheaperFragment, favorFragment, pocketFragmnet, moreFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
initFragment();
}
private void init() {
textView[0] = (TextView)findViewById(R.id.near);
textView[1] = (TextView)findViewById(R.id.search_cheaper);
textView[2] = (TextView)findViewById(R.id.favor);
textView[3] = (TextView)findViewById(R.id.pocket);
textView[4] = (TextView)findViewById(R.id.more);
views[0] = findViewById(R.id.near_top_line);
views[1] = findViewById(R.id.cheaper_top_line);
views[2] = findViewById(R.id.favor_top_line);
views[3] = findViewById(R.id.pocket_top_line);
views[4] = findViewById(R.id.more_top_line);
textView[0].setOnClickListener(this);
textView[1].setOnClickListener(this);
textView[2].setOnClickListener(this);
textView[3].setOnClickListener(this);
textView[4].setOnClickListener(this);
}
private void initFragment() {
firstFragment = FavorFragment.newInstance();
favorFragment = firstFragment;
// 最先加载的 fragment
getSupportFragmentManager().beginTransaction().
add(R.id.frame_layout, favorFragment).commit();
textView[2].setTextColor(Color.BLACK);
views[2].setBackgroundColor(Color.parseColor("#FF6600"));
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.near:
// getSupportFragmentManager().beginTransaction().
// replace(R.id.frame_layout, NearFragment.newInstance()).commit();
if(nearFragment==null){
nearFragment= NearFragment.newInstance();
}
switchContent(firstFragment, nearFragment, getSupportFragmentManager().beginTransaction());
firstFragment = nearFragment;
selectStringAndBackgroundColor(0);
break;
case R.id.search_cheaper:
if(cheaperFragment==null){
cheaperFragment= CheaperFragment.newInstance();
}
switchContent(firstFragment, cheaperFragment, getSupportFragmentManager().beginTransaction());
firstFragment = cheaperFragment;
selectStringAndBackgroundColor(1);
break;
case R.id.favor:
if(favorFragment==null){
favorFragment= FavorFragment.newInstance();
}
switchContent(firstFragment, favorFragment, getSupportFragmentManager().beginTransaction());
firstFragment = favorFragment;
selectStringAndBackgroundColor(2);
break;
case R.id.pocket:
if(pocketFragmnet==null){
pocketFragmnet= PocketFragment.newInstance();
}
switchContent(firstFragment, pocketFragmnet, getSupportFragmentManager().beginTransaction());
firstFragment = pocketFragmnet;
selectStringAndBackgroundColor(3);
break;
case R.id.more:
if(moreFragment==null){
moreFragment= MoreFragment.newInstance();
}
switchContent(firstFragment, moreFragment, getSupportFragmentManager().beginTransaction());
firstFragment = moreFragment;
selectStringAndBackgroundColor(4);
break;
}
}
/**
* 通过 position 的位置改变文字和 View 的颜色
* @param position
*/
private void selectStringAndBackgroundColor(int position){
int sum = textView.length;
for (int i = 0; i < sum; i++) {
if (position == i) {
textView[i].setTextColor(Color.BLACK);
views[i].setBackgroundColor(Color.parseColor("#FF6600"));
} else {
textView[i].setTextColor(Color.GRAY);
views[i].setBackgroundColor(Color.parseColor("#f0f0f0"));
}
}
}
/**
* 判断是否添加了界面,以保存当前状态
*/
public void switchContent(Fragment from, Fragment to,
FragmentTransaction transaction) {
if (!to.isAdded()) { // 先判断是否被add过
transaction.hide(from).add(R.id.frame_layout, to)
.commit(); // 隐藏当前的fragment,add下一个到Activity中
} else {
transaction.hide(from).show(to).commit(); // 隐藏当前的fragment,显示下一个
}
}
/**
* 由于 Activity 的启动模式为 singleTop ,搜索的 Action 必须为:
* android.intent.action.SEARCH,所以需要重写该方法,以便获取 Action 的“动作”。
* 如果不重写,Action 的返回值为 android.intent.action.MAIN
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 设置 intent 这样就能得到正确的 Action 了
setIntent(intent);
}
/**
* 该方法在 onNewIntent() 方法后执行,可以获取 onNewIntent() 方法设置的 intent,
* 也可以直接 onNewIntent() 方法进行得到 intent 等的操作
*/
@Override
protected void onResume() {
super.onResume();
Intent intent = getIntent();
if (intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
searchQurey(query, intent);
}
}
@Override
public void doSearch() {
onSearchRequested();
}
private void searchQurey(String query , Intent intent) {
//保存搜索记录
SearchRecentSuggestions suggestions=new SearchRecentSuggestions(this,
SearchSuggestionSampleProvider.AUTHORITY, SearchSuggestionSampleProvider.MODE);
// 点击 “清空历史记录” 删除记录
if(query.equals(getString(R.string.clear_search_keyword))){
suggestions.clearHistory();
}else{
suggestions.saveRecentQuery(query, null);
}
}
// private void clearSearchHistory() {
// SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
// SearchSuggestionSampleProvider.AUTHORITY, SearchSuggestionSampleProvider.MODE);
// suggestions.clearHistory();
// }
}
</span>