详细解读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
相关文章
相关标签/搜索