Android 中的 Searchable 使用,及删除记录

来源:互联网 发布:mac怎么远程协助 编辑:程序博客网 时间:2024/06/04 20:09

       Android为程序的搜索功能提供了统一的搜索接口,search dialog和search widget,这里简单介绍search dialog使用。search dialog 只能为于activity窗口的上方。下面以点击EditText输入框启动search dialog搜索框为例,效果如下:

       该效果是在点击 EditText 时,自动跳转到 google 自带的搜索框界面。 google 自带的搜索框,其左边的图标为该 app 的图标。无论是点击该图标或者是该图标前面的箭头都是可以回到 EditText 所在的界面。

       由于点击 EditText 会自动跳转界面,所以可以在跳转后的界面中自行布局,丰富界面。而本例是为了让其在当前界面实现跳转搜索,不需要额外的启动一个界面。所以在启动模式中使用“单顶模式”,即:singleTop 模式。一定要注意 google 自带的搜索框是位于 Activity 的顶部的(没办法修改)。


       实现效果图中的大致步骤:

     1. 在 res/xml目录下新建 searchable.xml 文件:

<span style="font-size:18px;"><?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></span>

  

        注意:在新建 searchable.xml 时,带上后缀名。上面的代码是最简单的搜索配置代码,其中的 android:lable="@string/app_name" 必须要有,且 lable 的属性一定要为 @string/app_name。 hint 属性是提示的信息,不是必须的,这点和 EditText 类似。其他的属性可以参考 google 的官方文档。



       2. 在 AndroidManifest.xml 中声明搜索的 Activity。

       由于本例不需要在搜索时启动一个 Activity 所以,将其设置为 singleTop 模式。所以:

在 activity 根标签外添加 :
<span style="font-size:18px;"><meta-data            android:name="android.app.default_searchable"            android:value=".MainActivity"/></span>

       在 activity 根标签内添加(因为启动模式为 singleTop 不会加载其他的界面,所以需要在 activity 标签内,如果需要在其他界面显示,则将其添加到注册该节目的 activity 根标签内):

<span style="font-size:18px;"><intent-filter>        <action android:name="android.intent.action.SEARCH" />   </intent-filter>   <meta-data        android:name="android.app.searchable"        android:resource="@xml/searchable" /></span>

       其中的 intent-filter 标签的 name 值必须为 android.intent.action.SEARCH,meta-data标签的 resource 值为 res/xml目录下的searchable.xml 文件;



       3. 启动搜索框:
       使用 onSearchRequested() 方法即可启动搜索框;

       当然也可以重写 onSearchRequested(),这样可以在搜索的时候去执行其他的操作,可以实现更多的扩展功能:
<span style="font-size:18px;">@Overridepublic boolean onSearchRequested() {   //做其他的事   doOther();   return super.onSearchRequested();}</span>

        如果想要传入参数:
<span style="font-size:18px;"> public boolean onSearchRequested() {          Bundle data = new Bundle();     data.putString("key", "your info");     startSearch(null, true, data , false);     return true; }</span>




         4. 取得搜索的关键字及处理搜索结果:
<span style="font-size:18px;">    /**     *  由于 Activity 的启动模式为 singleTop ,搜索的 Action 必须为:     *  android.intent.action.SEARCH,所以需要重写该方法,以便获取 Action 的“动作”。     *  如果不重写,Action 的返回值为 android.intent.action.MAIN     */    @Override    protected void onNewIntent(Intent intent) {        super.onNewIntent(intent);        // 设置 intent 这样就能得到正确的 Action 了        setIntent(intent);    }    /**     *  该方法在 onNewIntent() 方法后执行,可以获取 onNewIntent() 方法设置的 intent,     *  也可以直接 onNewIntent() 方法进行得到 intent 等的操作     */    @Override    protected void onResume() {        super.onResume();        Intent intent = getIntent();        if (intent.ACTION_SEARCH.equals(intent.getAction())) {            String query = intent.getStringExtra(SearchManager.QUERY);            searchQurey(query, intent);        }    }</span>

       在 Activity 中重写 onNewIntent() 方法,该方法为启动模式为 singleTop 下得到 Action 值为 android.intent.action.SEARCH 的intent,在 onNewIntent() 方法中设置 intent 即:setIntent(intent);可以重写 onResume() 方法,可在该方法中得到 onNewIntent() 方法中设置的 intent 即:getIntent();


       这样在增加相应的点击事件过后就能实现搜索了,当然了这还与我们的效果图还有差距。上面的步骤还没有实现记录搜索结果、查询搜索结果、删除搜索记录。




下面是实现其剩下的步骤:

       5. 使用 ContentProvider 来保存每次搜索的记录:
可以新建一个 SearchSuggestionSampleProvider 类继承 SearchRecentSuggestionsProvider 类,该类可用于保存最近的搜索等;
<span style="font-size:18px;">package com.crazy.gemi.ui.cheaper;import android.content.SearchRecentSuggestionsProvider;import android.database.Cursor;import android.database.MatrixCursor;import android.net.Uri;import android.os.CancellationSignal;import com.crazy.gemi.R;/** *  该类用于记录最近的搜索,并将其存入数据库 */public class SearchSuggestionSampleProvider        extends SearchRecentSuggestionsProvider {    public final static String AUTHORITY="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider";    public final static int MODE=DATABASE_MODE_QUERIES;    public SearchSuggestionSampleProvider(){        super();        setupSuggestions(AUTHORITY, MODE);    }}</span>
 
       该类的构造方法中使用 setupSuggestions(AUTHORITY, MODE);其中第一个参数为该类的权限名,第二个参数值为 DATABASE_MODE_QUERIES 常量(该常量为 1 )。

       由于该类本质上是 ContentProvider 所以需要注册:
<span style="font-size:18px;"> <provider            android:authorities="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider"            android:name=".ui.cheaper.SearchSuggestionSampleProvider"/></span>


        这样就完成了所有在 AndroidManifest.xml 中的工作,其中的完整代码如下:

<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.crazy.gemi">    <uses-permission android:name="android.permission.INTERNET"/>    <application        android:allowBackup="true"        android:icon="@mipmap/naryou_logo"        android:name=".ui.utils.MyApplication"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <meta-data            android:name="android.app.default_searchable"            android:value=".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>            <intent-filter>                <action android:name="android.intent.action.SEARCH" />            </intent-filter>            <meta-data                android:name="android.app.searchable"                android:resource="@xml/searchable" />        </activity>        <provider            android:authorities="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider"            android:name=".ui.cheaper.SearchSuggestionSampleProvider"/>    </application>    </manifest></span>


        现在还需要在 res/xml目录下的 searchable.xml 文件添加如下属性用于记录搜索记录和查询搜索记录:
<span style="font-size:18px;">android:searchSuggestAuthority="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider"    android:searchSuggestSelection=" ? "</span>

        其中:android:searchSuggestAuthority="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider"为保存搜索关键字时的 ContentProvider;android:searchSuggestSelection=" ? " 为下次输入时,自动查询以前搜索记录的字段,该参数必须要有。


        这样也就完成了 searchable.xml 的完整代码,代码如下:
<span style="font-size:18px;"><?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.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider"    android:searchSuggestSelection=" ? "></searchable></span>



       6. 保存搜索记录:
<span style="font-size:18px;">    private void searchQurey(String query , Intent intent) {        //保存搜索记录        SearchRecentSuggestions suggestions=new SearchRecentSuggestions(this,                SearchSuggestionSampleProvider.AUTHORITY, SearchSuggestionSampleProvider.MODE);        suggestions.saveRecentQuery(query, null);            }</span>





        这样通过上面的 6 大步骤就可以实现搜索记录的查询了。但是还差了删除记录的功能了。怎样实现删除功能呢?或许有多种方法。这里提供我的办法来实现该功能。



        7. 实现删除历史记录:

       要删除记录是不是需要在搜索记录的最低端要有“删除历史记录”这样的字眼呢?这是肯定的,那么就在其中插入该字眼。我们不可能在搜索框中输入“删除历史记录”这样的字眼吧,这样的行为就显得太傻了一样。我们的 SearchSuggestionSampleProvider 类不是继承 SearchRecentSuggestionsProvider 类吗?该类不是有 query() 方法可以提供查询吗,这样我们在每次的查询过程中就在其底部添加“删除历史记录”的字眼不就可以了吗。好,该类的完整代码如下:
SearchSuggestionSampleProvider.java (完整代码):
<span style="font-size:18px;">package com.crazy.gemi.ui.cheaper;import android.content.SearchRecentSuggestionsProvider;import android.database.Cursor;import android.database.MatrixCursor;import android.net.Uri;import android.os.CancellationSignal;import com.crazy.gemi.R;/** *  该类用于记录最近的搜索,并将其存入数据库 */public class SearchSuggestionSampleProvider        extends SearchRecentSuggestionsProvider {    public final static String AUTHORITY="com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider";    public final static int MODE=DATABASE_MODE_QUERIES;    public SearchSuggestionSampleProvider(){        super();        setupSuggestions(AUTHORITY, MODE);    }    @Override    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {        String[] items = null;        Cursor cursor = super.query(uri, projection, selection, selectionArgs, sortOrder);        int arrayLength = cursor.getCount();        if (arrayLength != 0) {            items = new String[arrayLength + 1];            cursor.moveToFirst();            int i = 0;            for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {                int number = cursor.getColumnIndex("suggest_intent_query");                items[i] = cursor.getString(number);                i++;            }        //    items[i] = "清空历史记录";            items[i] = getContext().getString(R.string.clear_search_keyword);        }else {            return null;        }        // suggest_format 等字段不能够改变        String[] columns = new String[]{"suggest_format", "suggest_text_1", "suggest_intent_query", "_id"};        // ContentProvider对外共享数据的时候,如果没有数据库,需要对外共享数据时则使用 MatrixCursor        MatrixCursor stringCursor = new MatrixCursor(columns);        String row[] = new String[4];        int i = 0;        for (CharSequence item : items) {            row[0] = "" + 0;            row[1] = item.toString();            row[2] = item.toString();            row[3] = "" + (++i);            stringCursor.addRow(row);        }        return stringCursor;    }}</span>



        这样是不是就可以实现删除功能了呢?如果按照上面的步骤去执行,那就会看见当我们每次点击“删除历史记录”时,就会在搜索框中多出“删除历史记录”这个字眼。由此可以我们并没有实现该功能,只实现了添加“删除历史记录”字眼的功能。到这一步离实现“删除历史记录”只有一步之遥的距离了。执行在 searchQurey() 方法中添加如下代码即可:

<span style="font-size:18px;"> // 点击 “清空历史记录” 删除记录        if(query.equals(getString(R.string.clear_search_keyword))){            suggestions.clearHistory();        }else{            suggestions.saveRecentQuery(query, null);        }</span>



         完整的 MainActivity.java 代码如下:

<span style="font-size:18px;">package com.crazy.gemi;import android.app.SearchManager;import android.content.Intent;import android.graphics.Color;import android.provider.SearchRecentSuggestions;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentActivity;import android.os.Bundle;import android.support.v4.app.FragmentTransaction;import android.view.View;import android.widget.TextView;import com.crazy.gemi.ui.cheaper.CheaperFragment;import com.crazy.gemi.ui.cheaper.SearchSuggestionSampleProvider;import com.crazy.gemi.ui.favor.FavorFragment;import com.crazy.gemi.ui.more.MoreFragment;import com.crazy.gemi.ui.near.NearFragment;import com.crazy.gemi.ui.pocket.PocketFragment;public class MainActivity extends FragmentActivity        implements View.OnClickListener, CheaperFragment.SearchResult{    private TextView[] textView = new TextView[5];    private View[] views = new View[5];    // 其中的 firstFragment 相当于是个中间变量    private Fragment firstFragment, nearFragment, cheaperFragment, favorFragment, pocketFragmnet, moreFragment;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        init();        initFragment();    }    private void init() {        textView[0] = (TextView)findViewById(R.id.near);        textView[1] = (TextView)findViewById(R.id.search_cheaper);        textView[2] = (TextView)findViewById(R.id.favor);        textView[3] = (TextView)findViewById(R.id.pocket);        textView[4] = (TextView)findViewById(R.id.more);        views[0] = findViewById(R.id.near_top_line);        views[1] = findViewById(R.id.cheaper_top_line);        views[2] = findViewById(R.id.favor_top_line);        views[3] = findViewById(R.id.pocket_top_line);        views[4] = findViewById(R.id.more_top_line);        textView[0].setOnClickListener(this);        textView[1].setOnClickListener(this);        textView[2].setOnClickListener(this);        textView[3].setOnClickListener(this);        textView[4].setOnClickListener(this);    }    private void initFragment() {        firstFragment = FavorFragment.newInstance();        favorFragment = firstFragment;        // 最先加载的 fragment        getSupportFragmentManager().beginTransaction().                add(R.id.frame_layout, favorFragment).commit();        textView[2].setTextColor(Color.BLACK);        views[2].setBackgroundColor(Color.parseColor("#FF6600"));    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.near://                getSupportFragmentManager().beginTransaction().//                        replace(R.id.frame_layout, NearFragment.newInstance()).commit();                if(nearFragment==null){                    nearFragment= NearFragment.newInstance();                }                switchContent(firstFragment, nearFragment, getSupportFragmentManager().beginTransaction());                firstFragment = nearFragment;                selectStringAndBackgroundColor(0);                break;            case R.id.search_cheaper:                if(cheaperFragment==null){                    cheaperFragment= CheaperFragment.newInstance();                }                switchContent(firstFragment, cheaperFragment, getSupportFragmentManager().beginTransaction());                firstFragment = cheaperFragment;                selectStringAndBackgroundColor(1);                break;            case R.id.favor:                if(favorFragment==null){                    favorFragment= FavorFragment.newInstance();                }                switchContent(firstFragment, favorFragment, getSupportFragmentManager().beginTransaction());                firstFragment = favorFragment;                selectStringAndBackgroundColor(2);                break;            case R.id.pocket:                if(pocketFragmnet==null){                    pocketFragmnet= PocketFragment.newInstance();                }                switchContent(firstFragment, pocketFragmnet, getSupportFragmentManager().beginTransaction());                firstFragment = pocketFragmnet;                selectStringAndBackgroundColor(3);                break;            case R.id.more:                if(moreFragment==null){                    moreFragment= MoreFragment.newInstance();                }                switchContent(firstFragment, moreFragment, getSupportFragmentManager().beginTransaction());                firstFragment = moreFragment;                selectStringAndBackgroundColor(4);                break;        }    }    /**     *  通过 position 的位置改变文字和 View 的颜色     * @param position     */    private void selectStringAndBackgroundColor(int position){        int sum = textView.length;        for (int i = 0; i < sum; i++) {            if (position == i) {                textView[i].setTextColor(Color.BLACK);                views[i].setBackgroundColor(Color.parseColor("#FF6600"));            } else {                textView[i].setTextColor(Color.GRAY);                views[i].setBackgroundColor(Color.parseColor("#f0f0f0"));            }        }    }    /**     * 判断是否添加了界面,以保存当前状态     */    public void switchContent(Fragment from, Fragment to,                              FragmentTransaction transaction) {        if (!to.isAdded()) { // 先判断是否被add过            transaction.hide(from).add(R.id.frame_layout, to)                    .commit(); // 隐藏当前的fragment,add下一个到Activity中        } else {            transaction.hide(from).show(to).commit(); // 隐藏当前的fragment,显示下一个        }    }    /**     *  由于 Activity 的启动模式为 singleTop ,搜索的 Action 必须为:     *  android.intent.action.SEARCH,所以需要重写该方法,以便获取 Action 的“动作”。     *  如果不重写,Action 的返回值为 android.intent.action.MAIN     */    @Override    protected void onNewIntent(Intent intent) {        super.onNewIntent(intent);        // 设置 intent 这样就能得到正确的 Action 了        setIntent(intent);    }    /**     *  该方法在 onNewIntent() 方法后执行,可以获取 onNewIntent() 方法设置的 intent,     *  也可以直接 onNewIntent() 方法进行得到 intent 等的操作     */    @Override    protected void onResume() {        super.onResume();        Intent intent = getIntent();        if (intent.ACTION_SEARCH.equals(intent.getAction())) {            String query = intent.getStringExtra(SearchManager.QUERY);            searchQurey(query, intent);        }    }    @Override    public void doSearch() {        onSearchRequested();    }    private void searchQurey(String query , Intent intent) {        //保存搜索记录        SearchRecentSuggestions suggestions=new SearchRecentSuggestions(this,                SearchSuggestionSampleProvider.AUTHORITY, SearchSuggestionSampleProvider.MODE);        // 点击 “清空历史记录” 删除记录        if(query.equals(getString(R.string.clear_search_keyword))){            suggestions.clearHistory();        }else{            suggestions.saveRecentQuery(query, null);        }    }//    private void clearSearchHistory() {//        SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,//                SearchSuggestionSampleProvider.AUTHORITY, SearchSuggestionSampleProvider.MODE);//        suggestions.clearHistory();//    }}</span>


       该类中有许多跟实现该功能无关的代码,如 CheaperFragment 中的接口回调等(由于 EditText 在CheaperFragment ,而 Fragment 中没有 onNewIntent() 的重写方法,所以需要使用接口)。这就需要读者自行去去掉了,呵呵!!







0 0
原创粉丝点击