標籤:android 懸浮搜尋方塊 searchview search
引言
在我們的應用程式中經常需要提供搜尋服務,比如搜尋連絡人, 搜尋商品資訊等等。我們可以自己在布局中自訂我們的搜尋方塊,實現我們的搜尋邏輯。但是還有一種更簡單的方法:使用android系統給我們提供的搜尋功能架構。
在android中,提供兩種實現搜尋功能的方式:search dialog 和 searchView.
search dialog類似於普通的dialog,懸浮於我們的表單之上。樣本圖如下:
searchView通常被嵌套在我們的布局之中,最典型的案例就是在actionBar中使用searchView.是searchView在中的使用。(PS:圖中的放大鏡就是searchView)
不管你使用哪種方式,安卓系統都會發送查詢請求到處理搜尋邏輯的activity中,來實現搜尋功能。
另外,除了普通的文字搜尋外,還提供了一下的搜尋功能:
1.語音搜尋
2.最近搜尋記錄提示
3.自訂搜尋記錄提示
4.google系統搜尋方塊
google系統搜尋方塊
需要注意的是:安卓系統並不會提供搜尋邏輯,也就是說,當系統將搜尋索引鍵傳遞給我們的時候,需要我們自己來處理搜尋邏輯。比如在資料庫中搜尋、在網路中搜尋。
另外,安卓系統也不會顯式地調用我們的搜尋方塊,我們需要自己調用方法來顯示我們的搜尋方塊。
今天我們主要介紹search dialog的使用方式。
基本原理
首先我們來瞭解一下系統搜尋功能的基本原理。
(一)當使用者在搜尋方塊中執行搜尋操作後,系統會自動建立一個Intent,並且將使用者搜尋的關鍵字存放到Intent中。
(二)系統會啟動處理搜尋邏輯的activity(通常可以命名為SearchableActivity)並將intent傳遞給SearchableActivity,然後在SearchableActivity中處理我們的搜尋邏輯。
配置搜尋方塊
第一步,我們需要配置我們搜尋方塊的xml檔案,其中包括一些屬性比如:語音搜尋,搜尋提示和搜尋記錄等等。
設定檔通常命名為searchable.xml 並且必須 存放在我們工程的res/xml目錄中(沒有就建立一個)
(PS:系統使用這個設定檔來執行個體化SearchableInfo對象,這個對象是提供搜尋相關的中繼資料的,比如SearchableActivity的類名,搜尋索引鍵的類型等等。但是我們不能自己執行個體化SearchableInfo 對象,只能通過設定檔的方式來設定)
下面是searchable.xml設定檔的內容
searchable.xml
<?xml version="1.0" encoding="utf-8"?><searchable xmlns:android="http://schemas.android.com/apk/res/android" android:hint="@string/searchHint" android:label="@string/searchLabel" ></searchable>
設定檔的根節點必須是searchable ,其中label是必須的,它的值為一個string資源引用,通常是應用程式的名稱(儘管它是一個必須的屬性,但通常情況下是不顯示出來的,除非你開啟了搜尋建議功能)。
android:hint是配置搜尋方塊的輸入提示資訊,雖然不是必須的屬性,但是強烈建議設定這個屬性,以便使用者輸入搜尋資訊的時候,可是知道能輸入那些搜尋資訊。
以配置很多的屬性,但大部分屬性都只是在使用搜尋建議和語音搜尋時進行配置。
SearchableActivity
第二步, 我們建立SearchableActivity來處理搜尋邏輯並且顯示搜尋結果。
我們需要在android-manifest.xml檔案中配置SearchableActivity的一些屬性,來將它指定為處理搜尋邏輯的activity
android-manifest.xml
<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的action。然後再meta-data節點中的name屬性必須為android.app.searchable,resource屬性為我們的設定檔searchable.xml
注意:我們並不需要在intent-filter中配置category,因為SearchManager會根據SearchableActivity的componentName,顯示地傳遞資料給它。
查看下列SearchManager.class的源碼,我們可以知道這是怎麼實現的。
SearchManager.class
/** * Starts the global search activity. */ /* package */ void startGlobalSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) { //這是我們的searchableActivity ComponentName globalSearchActivity = getGlobalSearchActivity(); if (globalSearchActivity == null) { Log.w(TAG, "No global search activity found."); return; } Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //顯示傳遞 intent.setComponent(globalSearchActivity); // Make sure that we have a Bundle to put source in if (appSearchData == null) { appSearchData = new Bundle(); } else { appSearchData = new Bundle(appSearchData); } // Set source to package name of app that starts global search, if not set already. if (!appSearchData.containsKey("source")) { appSearchData.putString("source", mContext.getPackageName()); } //查詢的資料 intent.putExtra(APP_DATA, appSearchData); if (!TextUtils.isEmpty(initialQuery)) { intent.putExtra(QUERY, initialQuery); } if (selectInitialQuery) { intent.putExtra(EXTRA_SELECT_QUERY, selectInitialQuery); } intent.setSourceBounds(sourceBounds); try { if (DBG) Log.d(TAG, "Starting global search: " + intent.toUri(0)); mContext.startActivity(intent); } catch (ActivityNotFoundException ex) { Log.e(TAG, "Global search activity not found: " + globalSearchActivity); } }
一般而言,查詢到的資料都是通過一個ListView來展示的,所以,我們可以讓SearchableActivity繼承ListActivity來方便操作。
在SearchableActivity中,我們需要完成三件事:
1.接受查詢參數
當使用者執行搜尋操作的時候,系統通過intent傳遞名為QUERY 的資料,其中包含的就是我們的搜尋索引鍵,我們可以在intent中接受QUERY資料
。
public class SearchActivity extends ListActivity{ //測試資料 private String[][] datas = { { "activity", "actionbar", "animation", "android" }, { "bundle", "block", "bluetooth", "boolean" } }; //查詢結果 private String[] result; private Intent intent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search); intent = getIntent(); // 判斷是否是搜尋請求 if (Intent.ACTION_SEARCH.equals(intent.getAction())) { // 擷取搜尋的查詢內容(關鍵字) String query = intent.getStringExtra(SearchManager.QUERY);
2.根據查詢參數查詢資料
擷取到查詢關鍵字query後,我們就可以執行我們的查詢邏輯了。
// 執行相應的查詢動作 boolean isSuccess =queryContact(query);
private boolean queryContact(String query) { for (String[] ss : datas) { for (String s : ss) { if (s.contains(query)){ result = ss; return true; } } } return false; }
queryContact方法是我寫的類比查詢字典的方法。這裡可以換成在資料庫或者網路中查詢資料。
3.顯示查詢到的資料
查詢到資料中,我們需要將資料顯示到ListView中,並且當使用者點擊某一查詢結果時,將查詢結果返回給MainActivity.
intent = new Intent(SearchActivity.this, MainActivity.class); if(isSuccess){ setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, result)); getListView().setOnItemClickListener( new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { intent.putExtra("name", result[position]); startActivity(intent); } }); }else{ Toast.makeText(this, "沒有查詢到資料", Toast.LENGTH_SHORT).show(); startActivity(intent); } } }
使用搜尋方塊
最後,我們就需要在MainActivity中使用我們的搜尋方塊了。由於前面說過,搜尋方塊預設情況下是隱藏的,需要我們自己來調用。在調用之前,我們還需要在manifest檔案中進行配置,指定使用searchableActivity.
<activity android:name=".MainActivity" android:label="@string/app_name" > <!-- enable the search dialog to send searches to SearchableActivity --> <meta-data android:name="android.app.default_searchable" android:value=".SearchActivity" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
在MianActivity的節點中,我們需要配置meta-data節點,name必須指定為android.app.default_searchable,value表示我們的searchableActivity.
如果想將搜尋方塊指定為全域的,在整個application中都能使用,那就將meta-data節點配置在application節點中。
最後,我們在MainActivity中調用搜尋方塊。
由於不同的裝置的物理按鍵有很大的差異,有些手機有物理的搜尋按鍵,而有些手機是沒有的。所以我們最好自己在activity中通過一個搜尋按鈕來顯式的調用搜尋方塊。另外一種方法是,通過手機軟鍵盤上面的搜尋按鈕來調用搜尋方塊,這需要在OnCreate()中調用 setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL) .
搜尋方塊是一個懸浮於螢幕上的dialog。它不會對activity棧和生命週期引起任何變化。所以當搜尋方塊出現的時候,沒有任何如onPause()的方法被調用。
通過調用onSearchRequested()方法,我們來啟用搜尋方塊。
在MainActivity中,我們點擊button來顯示搜尋方塊,執行搜尋後,將擷取到的搜尋結果顯示在TextView中。
MainActivity.class
public class MainActivity extends ActionBarActivity{ private TextView msg; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); msg=(TextView) findViewById(R.id.msg); } @Override protected void onResume() { String name =(String) getIntent().getStringExtra("name"); if(name!=null) msg.setText(name); super.onResume(); } public void search(View view) { onSearchRequested(); }}
另外,我們也可以重寫onSearchRequested()方法,在搜尋的同時做一些其他的操作,比如暫停音樂播放等等。
@Overridepublic boolean onSearchRequested() { pauseMusic(); return super.onSearchRequested();}
另外,如果我們需要對查詢關鍵字加一些限制條件的時候,我們可以調用onSearchRequested()發送一些額外的資料給searchableActivity,searchableActivity中進行處理。
@Overridepublic boolean onSearchRequested() {//查詢參數 Bundle appData = new Bundle(); appData.putBoolean(SearchableActivity.JARGON, true); startSearch(null, false, appData, false); return true; }
當searchableActivity接受到傳遞的查詢參數和關鍵字時,就可以進行查詢操作了。
//通過SearchManager.APP_DATA來提取資料Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA);if (appData != null) { boolean jargon = appData.getBoolean(SearchableActivity.JARGON); //下面這一句表示我們可以進行的操作。。。。 // “select * from ... where word=query and ...=jargon”;}
注意:我們不能再onSearchRequested()方法外調用startSearch方法,任何操作都必須通過onSearchRequested()來調用。
本文參考自android官網:https://developer.android.com/guide/topics/search/search-dialog.html
源碼下載
android浮動搜尋方塊的使用