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


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

 

一、Search Dialog 和 Search View

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

不同點:

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包中提供的控件)
 
二、搜索程序的構成
 
實現一個可以搜索的程序,主要需要以下幾個部份:
(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的任何地方。
 
 
三、編寫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的話,大多數的屬性是不需要的。未來可能有文章去專門介紹這個配置文件。
 
四、配置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>

為了方便解釋,我們直接看主要代碼:

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>

            <!-- 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的配置信息。

 
 
五、編寫這兩個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不用程序員進行過多干預。

 
擴展:
系統默認會將用戶輸入的信息傳遞到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。

 
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中獲得數據,然后進行處理。這里僅僅獲得了數據,沒有進行真正的搜索。

 
 
六、通過一個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
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM