先看結果:
相關的官方文檔在這里: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 - 完成搜索建議
文章列表