詳細解讀Android中的搜索框(二)—— Search Dialog

Search Dialog是提供搜索的控件之一,還有一個是上次小例子給出的searchView,關於SearchView的東西后面會說到。本次先從Search Dialog提及,讓你們慢慢理解android中搜索的控件的機制,逐漸引出搜索信息傳遞和搜索配置的知識,鋪墊到最後再給你們說searchview的話,你們就能很容易理解。 html

 

1、Search Dialog 和 Search Viewjava

這兩個其實都是一個搜索控件,區別不大,但多少仍是有些小的差別的。android

不一樣點:程序員

A:search dialog是一個被系統控制的UI組件。但他被用戶激活的時候,它「老是」出如今activity的上方,如圖所示:
B:Android系統本身負責處理search dialog上全部的事件,當用戶點擊查詢按鈕,系統會把這個查詢請求傳輸到咱們本身定義的的searchable activity,這個 searchable activity處理真正的查詢。當用戶在輸入的時候,search dialog還能提供搜索建議。(這點下文會有說起)
C:而SearchView其實就是一個view,你天然能夠把它放在你的佈局的任何地方。(但通常咱們仍是將其放在屏幕的上方)
D:默認的,searchView和一個標準的EditText同樣,不能作任何事情。 可是你能夠配置它,讓android系統處理全部的按鍵事件,把查詢請求傳輸給合適的activity,能夠配置它讓它像search dialog同樣提供search suggestions(搜索建議)。
E:search widget在 Android 3.0或更高版本纔可用. search dialog沒有此項限制。(若是要在低版本使用,須要用support包中提供的控件)
 
2、搜索程序的構成
 
實現一個能夠搜索的程序,主要須要如下幾個部份:
(1),search dialog or search view的配置文件。
配置一個XML文件用於配置search dialog 或widget的設置。對於search dialog,該配置文件的名字通常約定爲searchable.xml(推薦) 
(2)searchable Activity。
searchable activity用於接收搜索關鍵字,並進行數據搜索和顯示搜索結果。
(3)搜索框 (search dialog 或search View)
    * search dialog
      默認的,search dialog是隱藏。當咱們按下了SEARCH鍵或在程序中調用onSearchRequested(),它將出如今屏幕的上方.
    * search view 
  使用search widget的時候,你能夠把該搜索條放在咱們activity的任何地方。
 
 
3、編寫SearchDialog的配置文件
 
3.1 配置搜索框
接着咱們須要定義搜索框的配置文件,依據官方的建議,我選用了searchable做爲xml文件的名字。
該文件通常約定爲searchable.xml並位於res/xml/目錄下。
searchable.xml必須以<searchable> element 做爲根節點,且至少定義一個屬性。
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:hint="@string/search_hint"
    android:label="@string/app_name" 
    android:icon="@drawable/kale">

</searchable>
  3.1.1 android:label
  android:label是惟一必須定義的屬性。它指向一個字符串,它應該是應用程序的名字。因此我這裏直接用了app_name。 實際上該label也只有在search suggestions for Quick Search Box可用時纔可見。
  3.1.2 android:hint
  android:hint屬性不是必須,可是但願你們定義它。它是search box用戶輸入前輸入框中的提示語。
<searchable> 還有其餘的一些屬性。若是不須要search suggestions 和voice search的話,大多數的屬性是不須要的。將來可能有文章去專門介紹這個配置文件。
 
4、配置Activity
 
個人思路是一個activity用於給用戶提供輸入,用戶點擊搜索後跳轉到另外一個activity開始執行搜索。提供用戶輸入的Activity叫作MainActivity,真正執行搜索的Activity叫作SearchActivity。下面是它們在manifest中的定義:
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTop" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <!-- enable the search dialog to send searches to SearchableActivity -->
            <meta-data
                android:name="android.app.default_searchable"
                android:value="com.kale.searchdialogtest.SearchActivity" />
        </activity>
        
        <activity
            android:name="com.kale.searchdialogtest.SearchActivity"
            android:launchMode="singleTop" >
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>

            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />
        </activity>

爲了方便解釋,咱們直接看主要代碼:app

MainActivity:ide

    <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>

            <!-- enable the search dialog to send searches to SearchableActivity -->
            <meta-data
                android:name="android.app.default_searchable"
                android:value="com.kale.searchdialogtest.SearchActivity" />
        </activity>
這個代碼中用了singleTop來設置activity,官方建議是用於搜索和展現信息的activity用singleTop定義,這裏的mainActivity雖然僅僅是提供輸入的,但爲了後續的例子,我仍是用了singleTop模式。而後就是設定它是啓動時第一展現的activity。下面重點來了。
            <!-- enable the search dialog to send searches to SearchableActivity -->
            <meta-data
                android:name="android.app.default_searchable"
                android:value="com.kale.searchdialogtest.SearchActivity" />

 

咱們知道,當用戶提交搜索結果的時候,系統就啓動一個咱們定義的searchable activity(就是這個例子中的SearchActivity) ,並把搜索關鍵字用一個aciton(名字爲CTION_SEARCH的Intent傳給你的searchable activity)。這樣,在searchable activity就可讓Intent中經過extra的QUERY來提取搜索關鍵字,執行搜索並顯示搜索結果。那麼如何讓系統知道提交搜索時,是啓動哪一個activity呢?就是經過上面這兩行配置文件作的。
要求:
1. 必須包含「android:value」屬性,該屬性指明瞭searchable activity的類名,
2. 必須包括屬性「android:name",且其值必須爲 "android.app.default_searchable".
 
這樣系統就知道用戶在提交搜索結果(通常是按下輸入法上的回車/搜索按鈕)時,應該啓動那個activity了。
 
SearchActivity
<activity
            android:name="com.kale.searchdialogtest.SearchActivity"
            android:launchMode="singleTop" >
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>

            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />
        </activity>

依據建議,用於展現搜索結果的activity應該用singleTop模式,同時要強制寫上以下內容。佈局

       <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>

            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />

 

若是對activity的隱式啓動有所瞭解的話,咱們一眼就看出爲何要這麼定義了。在MainActivity中系統會在用戶提交搜索時產生一個intent,而且給intent放入搜索詞,並且還定義了一個action。系統這時就開始找哪一個activity中定義了 <action android:name="android.intent.action.SEARCH" />,找到這個activity後就會自動啓動咱們的這個searchActivity。至於meta-data中的東西,其實就是一個search的配置信息。ui

 
 
5、編寫這兩個Activity中的代碼
 
1. MainActivity
package com.kale.searchdialogtest;public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btn = (Button) findViewById(R.id.show_dialog_button);
        btn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                onSearchRequested();
            }
        });

    }

}

 很簡單吧,經過onSearchRequested()咱們就可讓activity中顯示出一個search dialog,因此在某種意義上說,search dialog不用程序員進行過多幹預。this

 
擴展:
系統默認會將用戶輸入的信息傳遞到searchActivity中,在searchActivity中咱們用過intent就能夠獲得這個輸入信息了。但若是咱們但願順便傳遞另一些信息呢?這時就須要重寫onSearchRequested方法了。
package com.kale.searchdialogtest;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btn = (Button) findViewById(R.id.show_dialog_button);
        btn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                onSearchRequested();

            }
        });

    }
// 重寫onSearchRequested方法
    @Override
    public boolean onSearchRequested() {
        // 除了輸入查詢的值,還可額外綁定一些數據
        Bundle appSearchData = new Bundle();
        appSearchData.putString("KEY", "text");

        startSearch(null, false, appSearchData, false);
        // 必須返回true。不然綁定的數據做廢
        return true;
    }



}

 咱們在這裏面放入了一個鍵值對,KEY-text。spa

 
2.SearchActivity
package com.kale.searchdialogtest;

import android.app.Activity;
import android.app.SearchManager;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

/**
 * @author:Jack Tony
 * @description :真正執行搜索和結果展現的Activity 一旦用戶在search dialog中執行search操做,
 *              系統將啓動SearchableActivity 並向其傳送ACTION_SEARCH intent.
 * @date :2015年1月15日
 * 
 * 參考自:http://zhouyunan2010.iteye.com/blog/1134147
 */
public class SearchActivity extends Activity {

    protected void onCreate(android.os.Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.search_activity);

        // Get the intent, verify the action and get the query
        Intent intent = getIntent();
        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra(SearchManager.QUERY);

            doMySearch(query);
        }

        // 得到額外遞送過來的值
        Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
        if (appData != null) {
            String testValue = appData.getString("KEY");
            System.out.println("extra data = " + testValue);
        }

    }

    private void doMySearch(String query) {
        // TODO 自動生成的方法存根
        TextView textView = (TextView) findViewById(R.id.search_result_textView);
        textView.setText(query);
        Toast.makeText(this, "do search", 0).show();
    }
}

 主要內容是從intent中得到數據,而後進行處理。這裏僅僅得到了數據,沒有進行真正的搜索。

 
 
6、經過一個activity進行搜索和展現
 
上面演示的是用兩個activity,一個進行輸入,一個進行處理,那麼若是我想用一個activity又進行輸入,又進行處理呢?其實也很簡單,二合一便可。
manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.kale.searchdialogtest"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <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>
        
    </application>

</manifest>

 

主要代碼:
     <activity
            android:name=".MainActivity" >

<intent-filter> <action android:name="android.intent.action.SEARCH" /> </intent-filter> <meta-data android:name="android.app.searchable" android:resource="@xml/searchable" /> </activity>
由於這個activity有處理搜索結果的能力,因此就必須定義
            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />
你可能會問,爲何不寫
       <meta-data
                android:name="android.app.default_searchable"
                android:value="com.kale.searchdialogtest.MainActivity" />
由於這個activity自身就已經聲明瞭本身有處理信息的能力,因此不用重複定義了。
 
MainActivity.java
package com.kale.searchdialogtest;

import android.app.SearchManager;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handleIntent(getIntent());
         
        Button btn = (Button) findViewById(R.id.show_dialog_button);
        btn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                onSearchRequested();
            }
        });
    }

    
    private void handleIntent(Intent intent) {
        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
          String query = intent.getStringExtra(SearchManager.QUERY);
          doMySearch(query);
        }
    }

    private void doMySearch(String query) {
        // TODO 自動生成的方法存根
        Toast.makeText(this, "do search " + query, 0).show();
    }

}
和以前的同樣,開始搜索後會啓動一個activity,只不過啓動的仍是當前的activity,並且當前棧中會有兩個mainActivity,爲了處理搜索信息,須要在activity的oncreate中捕獲intent。但這樣的效果多多少少會讓用戶感受不爽,因此咱們須要進行以下的改動。
 
擴展:
 
用singleTop來設計MainActivity
 
manifest.xml
 <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            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>

 

這裏用了單例模式就可讓activity不重複啓動了,那麼問題就來了。不重複啓動的話,如何接收intent呢?下面來解決這個問題。
 
MainActivity.java
package com.kale.searchdialogtest;

import android.app.SearchManager;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

/**
 * @author:Jack Tony
 * @description :
 * 
 *              當系統調用onNewIntent(Intent)的時候,表示activity並非新建的, 因此getIntent()返回的仍是
 *              在onCreate()中接受到的intent.
 *              所以你必須在onNewIntent(Intent)調用setIntent(Intent)來
 *              (這樣保存的intent才被更新,以後你能夠同過getIntent()來取得它).
 * 
 * @date :2015年1月15日
 */
public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btn = (Button) findViewById(R.id.show_dialog_button);
        btn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                onSearchRequested();
            }
        });

    }

 @Override protected void onNewIntent(Intent intent) { setIntent(intent); handleIntent(intent); } private void handleIntent(Intent intent) {
        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra(SearchManager.QUERY);
            doMySearch(query);
        }
    }

    private void doMySearch(String query) {
        // TODO 自動生成的方法存根
        Toast.makeText(this, "do search " + query, 0).show();
    }

}

 

我經過onNewIntent讓activity更新下intent,這樣就能夠接收到本身傳給本身的數據了。當系統調用onNewIntent(Intent)的時候,表示activity並非新建的, 因此getIntent()返回的仍是onCreate()中接受到的intent. 所以你必須在onNewIntent(Intent)調用setIntent(Intent)來 (這樣保存的intent才被更新,以後你能夠同過getIntent()來取得它)
 
 
 
 
參考自:
http://blog.csdn.net/hudashi/article/details/7052815
http://blog.csdn.net/hudashi/article/details/7052824
http://blog.csdn.net/hudashi/article/details/7052831
http://blog.csdn.net/hudashi/article/details/7052840
相關文章
相關標籤/搜索