MaterialDesign 之 SearchView 全面解鎖

GitLqr發表於2017-05-19

一、簡述

SearchView是v7包中的一個相容性控制元件,它可以單獨使用,也可以配合menu+toolbar一起使用。本文將使用第二種方式,對SearchView進行探索。最後將通過程式碼實戰,實現 “仿網易雲音樂本地音樂搜尋” 效果,帶你全面解鎖SearchView的UI定製及查詢功能的實現。

二、常規使用

*本文重點是SearchView,所以對Toolbar的使用及注意事項在本文中將不會有過多的體現,如需瞭解可自行百度或直接檢視本Demo原始碼(原始碼在文末)。

1、在menu的xml檔案中配置

要跟menu一起使用,就必須在menu的xml檔案中對其中的一個item進行actionViewClass屬性配置,如:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools">
    <item
        android:id="@+id/menu_search"
        android:orderInCategory="100"
        android:title="搜尋"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="always"
        />
    ...
</menu>複製程式碼

這個item跟普通item的差別在於使用了app:actionViewClass屬性(注意是app:開頭的!!!),這裡使用的是相容性控制元件裡的SearchView,所以取值"android.support.v7.widget.SearchView"。其中title的設定不會生效(一般設定了title的item,長按後會有彈出提示文字),這裡去掉也無所謂。

2、在onCreateOptionsMenu()中得到SearchView

我們知道menu在建立時會回撥Activity中的onCreateOptionsMenu(Menu menu)方法,通過該方法可以得到Menu物件,而SearchView是Menu中item的一個actionView,actionView可以通過MenuItemCompat獲取,故,通過Menu物件可以得到SearchView。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.search_view, menu);
    MenuItem searchItem = menu.findItem(R.id.menu_search);
    //通過MenuItem得到SearchView
    mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
    ...
    return super.onCreateOptionsMenu(menu);
}複製程式碼

到這裡就可以看到效果了:

MaterialDesign 之 SearchView 全面解鎖

3、對SearchView進行設定

預設展開搜尋框

手淘的首頁搜尋框是預設展開的,使用SearchView可以做一樣的效果。此外,SearchView有三種預設展開的設定,效果上有略微不同,請結合註釋與圖片仔細觀察。

/*------------------ SearchView有三種預設展開搜尋框的設定方式,區別如下: ------------------*/
//設定搜尋框直接展開顯示。左側有放大鏡(在搜尋框中) 右側有叉叉 可以關閉搜尋框
mSearchView.setIconified(false);
//設定搜尋框直接展開顯示。左側有放大鏡(在搜尋框外) 右側無叉叉 有輸入內容後有叉叉 不能關閉搜尋框
mSearchView.setIconifiedByDefault(false);
//設定搜尋框直接展開顯示。左側有無放大鏡(在搜尋框中) 右側無叉叉 有輸入內容後有叉叉 不能關閉搜尋框
mSearchView.onActionViewExpanded();複製程式碼

按順序效果依次如下:

MaterialDesign 之 SearchView 全面解鎖
setIconified(false)

MaterialDesign 之 SearchView 全面解鎖
setIconifiedByDefault(false)

MaterialDesign 之 SearchView 全面解鎖
onActionViewExpanded()

SearchView的常規設定

//設定最大寬度
mSearchView.setMaxWidth(500);
//設定是否顯示搜尋框展開時的提交按鈕
mSearchView.setSubmitButtonEnabled(true);
//設定輸入框提示語
mSearchView.setQueryHint("hint");複製程式碼

比較容易,直接看下效果:

MaterialDesign 之 SearchView 全面解鎖

SearchView的事件監聽

SearchView提供的事件監聽還是比較豐富的,一般常用的有開啟搜尋框按鈕的點選事件、清空或關閉搜尋框按鈕的點選事件、搜尋框文字變化事件等。

//搜尋框展開時後面叉叉按鈕的點選事件
mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
    @Override
    public boolean onClose() {
        Toast.makeText(getApplicationContext(), "Close", Toast.LENGTH_SHORT).show();
        return false;
    }
});
//搜尋圖示按鈕(開啟搜尋框的按鈕)的點選事件
mSearchView.setOnSearchClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(getApplicationContext(), "Open", Toast.LENGTH_SHORT).show();
    }
});
//搜尋框文字變化監聽
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String s) {
        Log.e("CSDN_LQR", "TextSubmit : " + s);
        return false;
    }

    @Override
    public boolean onQueryTextChange(String s) {
        Log.e("CSDN_LQR", "TextChange --> " + s);
        return false;
    }
});複製程式碼

這個也比較容易,直接看下效果:

MaterialDesign 之 SearchView 全面解鎖

以上就是SearchView給開發者提供的常規方法呼叫和屬性設定,但是這並不能滿足我們的開發需求,因為開發中大部分設計師根據不管MaterialDesign的設計規範,所以大多數情況下需要根據UI設計稿自定義SearchView的樣式了,這相對比較複雜,下面將通過實戰來學習SearchView的樣式自定義,以此來滿足我們的開發需求。

三、實戰

仿網易雲音樂本地音樂搜尋,先看下效果,然後開始實戰:

MaterialDesign 之 SearchView 全面解鎖

1、設定Toolbar

1)建立該介面的佈局activity_search_view2.xml

指定Toolbar的高度、NaviagtionIcon、標題、字型等

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        style="@style/Toolbar.MyStyle"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:navigationIcon="@mipmap/lg"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        app:title="本地音樂"
        app:titleTextAppearance="@style/Toolbar.TitleText"
        app:titleTextColor="@android:color/white"/>
</LinearLayout>複製程式碼

其中style指向的Toolbar.MyStyle是設定標題與NavigationIcon的距離,titleTextAppearance指向的Toolbar.TitleText是設定標題文字大小。

在style.xml中建立Toolbar的自定義樣式

<!--標題與NavigationIcon的距離-->
<style name="Toolbar.MyStyle" parent="Base.Widget.AppCompat.Toolbar">
    <item name="contentInsetStart">0dp</item>
    <item name="contentInsetStartWithNavigation">0dp</item>
</style>

<!--Toolbar標題文字大小-->
<style name="Toolbar.TitleText" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
    <item name="android:textSize">15sp</item>
</style>複製程式碼

如果不設定的話,效果不好,NavigationIcon和Toolbar的標題之前的間距看起來很大,下面看下設定前後的差別:

MaterialDesign 之 SearchView 全面解鎖

MaterialDesign 之 SearchView 全面解鎖

2)設定去除ActionBar的主題

在Style.xml中建立無ActionBar的主題,並設定主題背景色

<style name="AppTheme.NoActionBar2" parent="AppTheme">
    <item name="colorPrimary">#D33A31</item>
    <item name="colorPrimaryDark">#D33A31</item>
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <!--設定menu中item的圖示顏色-->
    <item name="android:textColorSecondary">#ffffff</item>
</style>複製程式碼

不設定textColorSecondary的話,預設menu的item圖示是黑色,下面看下設定前後的差別:

MaterialDesign 之 SearchView 全面解鎖

MaterialDesign 之 SearchView 全面解鎖

為Activity設定主題

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.lqr.materialdesigndemo">

    <application
        ...
        android:theme="@style/AppTheme">
        ...

        <activity
            android:name=".SearchViewActivity2"
            android:screenOrientation="portrait"
            android:theme="@style/AppTheme.NoActionBar2"/>
    </application>
</manifest>複製程式碼

3)在Activity中設定Toolbar的程式碼如下:

public class SearchViewActivity2 extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search_view2);
        // 使用Toolbar代替actionbar
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }
    ...
}複製程式碼

2、設定Menu

1)建立選單佈局search_view.xml

跟之前的程式碼相比,只是多加了幾個item而已。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools">
    <item
        android:id="@+id/menu_search"
        android:orderInCategory="100"
        android:title="搜尋"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="always"
        />
    <item
        android:id="@+id/scan_local_music"
        android:icon="@mipmap/lv"
        android:orderInCategory="100"
        android:title="掃描本地音樂"
        app:showAsAction="never"
        />
    <item
        android:id="@+id/select_sort_way"
        android:icon="@mipmap/lt"
        android:orderInCategory="100"
        android:title="選擇排序方式"
        app:showAsAction="never"
        />
    <item
        android:id="@+id/get_cover_lyrics"
        android:icon="@mipmap/lq"
        android:orderInCategory="100"
        android:title="獲取封面歌詞"
        app:showAsAction="never"
        />
    <item
        android:id="@+id/imporve_tone_quality"
        android:icon="@mipmap/lw"
        android:orderInCategory="100"
        android:title="升級音質"
        app:showAsAction="never"
        />
</menu>複製程式碼

2)在Activity中設定Menu的程式碼如下:

public class SearchViewActivity2 extends AppCompatActivity {

    private SearchView mSearchView;
    ...
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.search_view, menu);
        MenuItem searchItem = menu.findItem(R.id.menu_search);

        //通過MenuItem得到SearchView
        mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);

        return super.onCreateOptionsMenu(menu);
    }

    // 讓選單同時顯示圖示和文字
    @Override
    public boolean onMenuOpened(int featureId, Menu menu) {
        if (menu != null) {
            if (menu.getClass().getSimpleName().equalsIgnoreCase("MenuBuilder")) {
                try {
                    Method method = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
                    method.setAccessible(true);
                    method.invoke(menu, true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return super.onMenuOpened(featureId, menu);
    }
}複製程式碼

到這裡,除了搜尋框(SearchView)以外,整個佈局的效果大體上都實現了:

MaterialDesign 之 SearchView 全面解鎖

3、定製SearchView樣式

接下來要實現的樣式自定義有:

MaterialDesign 之 SearchView 全面解鎖

重點來了,我們先來分析一下。SearchView本身不向外提供 “關閉搜尋框” 和 “設定搜尋框左邊的搜尋圖示” 等方法,所以需要通過其他的方式來實現樣式自定義。

考慮:

如果SearchView的佈局結構是通過xml佈局檔案來實現的,那麼可以通過SearchView.findViewById()的方式得到其中的部分或所有的控制元件;如果是通過程式碼動態新增的話,那麼可以通過反射的方式得到我們需要的控制元件,進而對控制元件進行樣式設定。

結論:

實現證明,SearchView的佈局結構就是使用xml佈局檔案實現的,該xml檔名為abc_search_view.xml,且基本上每個控制元件都有id,這樣就可以拿到需要的控制元件來實現樣式自定義了。

1)點選返回按鈕,退出搜尋框(若搜尋框顯示的話)

SearchView本身沒有提供關閉搜尋框的方法(反正我是沒找到啊~~),不過SearchView中正好有一個onCloseClicked()方法是用來關閉搜尋框,我們可以通過反射來呼叫該方法,先來理解下該方法都做了什麼,onCloseClicked()的程式碼如下:

void onCloseClicked() {
    Editable text = this.mSearchSrcTextView.getText();
    //如果搜尋框中有文字,則清除其中的文字
    if(TextUtils.isEmpty(text)) {
        if(this.mIconifiedByDefault && (this.mOnCloseListener == null || !this.mOnCloseListener.onClose())) {
            this.clearFocus();
            this.updateViewsVisibility(true);
        }
    } 
    //否則關閉搜尋框
    else {
        this.mSearchSrcTextView.setText("");
        this.mSearchSrcTextView.requestFocus();
        this.setImeVisibility(true);
    }
}複製程式碼

這裡要考慮到,當搜尋框顯示時,按下Toolbar的返回按鈕關閉搜尋框,否則就關閉當前介面。因為搜尋框也有id,所以我們可以通過id可以得到搜尋框控制元件,用來判斷當前搜尋框的顯隱狀態。結合SearchView內部的onCloseClicked()方法,最後Toolbar返回按鈕的點選事件程式碼可以這麼寫:

public class SearchViewActivity2 extends AppCompatActivity {

    private SearchView mSearchView;
    private SearchView.SearchAutoComplete mSearchAutoComplete;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search_view2);
        ...
        //Toolbar返回按鈕的點選事件
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mSearchAutoComplete.isShown()) {
                    try {
                        //如果搜尋框中有文字,則會先清空文字,但網易雲音樂是在點選返回鍵時直接關閉搜尋框
                        mSearchAutoComplete.setText("");
                        Method method = mSearchView.getClass().getDeclaredMethod("onCloseClicked");
                        method.setAccessible(true);
                        method.invoke(mSearchView);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    finish();
                }
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.search_view, menu);
        MenuItem searchItem = menu.findItem(R.id.menu_search);

        //通過MenuItem得到SearchView
        mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
        //通過id得到搜尋框控制元件
        mSearchAutoComplete = (SearchView.SearchAutoComplete) mSearchView.findViewById(R.id.search_src_text);

        return super.onCreateOptionsMenu(menu);
    }

    ...
}複製程式碼

MaterialDesign 之 SearchView 全面解鎖

2)隱藏搜尋框左邊的搜尋圖示

搜尋框中左邊的搜尋圖示不是一個控制元件,所以沒辦法通過id得到,但好在可以通過設定style的方式來修改SearchView所有的圖示。方法也很簡單,只需建立一個style(這裡取名Widget.SearchView)繼承自Widget.AppCompat.SearchView,然後替換需要修改的屬性即可。先看下Widget.AppCompat.SearchView的父級Base.Widget.AppCompat.SearchView吧:

MaterialDesign 之 SearchView 全面解鎖

可以看到,這個父級style提供了SearchView中幾乎所有的Icon屬性,這意味著在圖示定製上可以有很大擴充性。其中,layout是指定SearchView的佈局,原始佈局就是abc_search_view.xml,我們一般不會去動這個屬性。
這裡我們只需要去掉搜尋框左邊的圖示(即:searchHintIcon),直接設定為@null就好了,如下修改style檔案中的Widget.SearchView主題:

<!--沒有ActionBar主題,自定義SearchView樣式-->
<style name="AppTheme.NoActionBar2" parent="AppTheme">
    ...
    <!--引入SearchView的自定義樣式-->
    <item name="searchViewStyle">@style/Widget.SearchView</item>
</style>

<style name="Widget.SearchView" parent="Widget.AppCompat.SearchView">
    <!--修改搜尋框提示文字-->
    <item name="defaultQueryHint">搜尋本地歌曲</item>
    <!--修改開啟搜尋框的搜尋按鈕的圖示-->
    <item name="searchIcon">@mipmap/m5</item>
    <!--修改搜尋框左邊的搜尋按鈕圖示-->
    <item name="searchHintIcon">@null</item>
</style>複製程式碼

MaterialDesign 之 SearchView 全面解鎖

3)設定搜尋框的提示文字

修改提示文字內容

修改搜尋框提示文字的方式有兩種,一種就是修改SearchView的style,如上一步中,修改Widget.AppCompat.SearchView的defaultQueryHint屬性;另一種方式是呼叫SearchView的setQueryHint()來修改。這兩種方式都可以,如果同時用這兩種方式來設定搜尋框的提示語,則最終的提示內容將以程式碼設定方式為主。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.search_view, menu);
    MenuItem searchItem = menu.findItem(R.id.menu_search);

    //通過MenuItem得到SearchView
    mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
    mSearchAutoComplete = (SearchView.SearchAutoComplete) mSearchView.findViewById(R.id.search_src_text);

    //通過程式碼方式修改提示文字內容
    mSearchView.setQueryHint("搜尋本地歌曲by code");

}複製程式碼

MaterialDesign 之 SearchView 全面解鎖

修改提示文字樣式

SearchView也沒有提供任何直接修改搜尋框提示文字樣式的方法,但既然我們可以通過id得到搜尋框控制元件,那設定提示文字的樣式便不是什麼問題了,程式碼如下:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.search_view, menu);
    MenuItem searchItem = menu.findItem(R.id.menu_search);

    //通過MenuItem得到SearchView
    mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
    mSearchAutoComplete = (SearchView.SearchAutoComplete) mSearchView.findViewById(R.id.search_src_text);
    mSearchView.setQueryHint("搜尋本地歌曲by code");

    //設定輸入框提示文字樣式
    mSearchAutoComplete.setHintTextColor(getResources().getColor(android.R.color.darker_gray));
    mSearchAutoComplete.setTextColor(getResources().getColor(android.R.color.background_light));
    mSearchAutoComplete.setTextSize(14);

    return super.onCreateOptionsMenu(menu);
}複製程式碼

MaterialDesign 之 SearchView 全面解鎖

4)根據搜尋框中有無文字,來顯隱搜尋框右邊的叉叉

這個有點像searchView.onActionViewExpanded()的效果,唯一的區別就是搜尋框不能是預設展開的,這要怎麼辦呢?通過觀察onActionViewExpanded()的原始碼,可以發現該方法中呼叫了setIconified(false)!!!再聯想到setIconified(false)本身就有讓搜尋框預設展開的效果,這是不是意味著,只要讓onActionViewExpanded()的setIconified(false)改為setIconified(true)就好了呢?答案是是的。而且不需要重寫SearchView,因為onActionViewExpanded()和setIconified(true)是可以搭配使用的,只要依次呼叫這兩個方法就可以實現這種效果了,程式碼如下:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.search_view, menu);
    MenuItem searchItem = menu.findItem(R.id.menu_search);

    //通過MenuItem得到SearchView
    mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
    mSearchAutoComplete = (SearchView.SearchAutoComplete) mSearchView.findViewById(R.id.search_src_text);
    mSearchView.setQueryHint("搜尋本地歌曲by code");

    //設定輸入框提示文字樣式
    mSearchAutoComplete.setHintTextColor(getResources().getColor(android.R.color.darker_gray));
    mSearchAutoComplete.setTextColor(getResources().getColor(android.R.color.background_light));
    mSearchAutoComplete.setTextSize(14);

    //設定搜尋框有字時顯示叉叉,無字時隱藏叉叉
    mSearchView.onActionViewExpanded();
    mSearchView.setIconified(true);

    //修改搜尋框控制元件間的間隔(這樣只是為了在細節上更加接近網易雲音樂的搜尋框)
    LinearLayout search_edit_frame = (LinearLayout) mSearchView.findViewById(R.id.search_edit_frame);
    ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) search_edit_frame.getLayoutParams();
    params.leftMargin = 0;
    params.rightMargin = 0;
    search_edit_frame.setLayoutParams(params);
    return super.onCreateOptionsMenu(menu);
}複製程式碼

MaterialDesign 之 SearchView 全面解鎖

4、實現搜尋提示功能

上面我們已經學習了SearchView的UI定製,下面將通過SearchView自身或結合ListView的方式(RecyclerView應該也一樣吧,還沒試過)直接學習SearchView搜尋提示功能的實現,繼續完善 “仿網易雲音樂本地音樂搜尋” 效果。

1)彈出式搜尋提示

SearchView本身的搜尋框就是AutoCompleteTextView的一個子類,有圖有真相。

MaterialDesign 之 SearchView 全面解鎖

MaterialDesign 之 SearchView 全面解鎖

AutoCompleteTextView是可以通過設定介面卡來實現文字補全提示功能的,所以,SearchView中的搜尋框一樣也可以,不過SearchView提供了setSuggestionsAdapter()方法可以直接為搜尋框設定介面卡,需要注意的是,這個介面卡必須跟資料庫的Cursor物件一起使用,例如:

mSearchView.setSuggestionsAdapter(new SimpleCursorAdapter(SearchViewActivity2.this, R.layout.item_layout, cursor, new String[]{"name"}, new int[]{R.id.text1}));複製程式碼

一般開發中遇到的需求是一邊輸入關鍵字一邊顯示搜尋結果,所以需要監聽搜尋框的文字輸入,一旦文字變化就查詢資料庫,更新搜尋結果,所以程式碼可以這麼寫:

// 監聽搜尋框文字變化
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String s) {
        return false;
    }

    @Override
    public boolean onQueryTextChange(String s) {
        Cursor cursor = TextUtils.isEmpty(s) ? null : queryData(s);
        // 不要頻繁建立介面卡,如果介面卡已經存在,則只需要更新介面卡中的cursor物件即可。
        if (mSearchView.getSuggestionsAdapter() == null) {
            mSearchView.setSuggestionsAdapter(new SimpleCursorAdapter(SearchViewActivity2.this, R.layout.item_layout, cursor, new String[]{"name"}, new int[]{R.id.text1}));
        } else {
            mSearchView.getSuggestionsAdapter().changeCursor(cursor);
        }

        return false;
    }
});複製程式碼

對於SimpleCursorAdapter的使用,不熟悉的自己百度學習吧,下面看效果:

MaterialDesign 之 SearchView 全面解鎖

可以發現,當輸入第一個文字"a"時,沒有什麼反應,當輸入第二個文字"a"時,彈出了一個列表彈窗,這是由於AutoCompleteTextView本身預設觸發查詢動作的條件就是該控制元件中的文字至少要2個以上,如果我們想修改成只要有一個文字就觸發查詢的話,則可以這麼做:

  1. 拿到SearchView中搜尋框控制元件
  2. 呼叫setThreshold()設定觸發查詢的字數

直接上程式碼:

 @Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.search_view, menu);
    MenuItem searchItem = menu.findItem(R.id.menu_search);

    //通過MenuItem得到SearchView
    mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
    mSearchAutoComplete = (SearchView.SearchAutoComplete) mSearchView.findViewById(R.id.search_src_text);
    ...
    //設定觸發查詢的最少字元數(預設2個字元才會觸發查詢)
    mSearchAutoComplete.setThreshold(1);
}複製程式碼

再看下效果:

MaterialDesign 之 SearchView 全面解鎖

好了,彈出式搜尋功能做完了,下面貼出條目佈局item_layout.xml和queryData()方法的程式碼實現:

① item_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <TextView
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:minHeight="?android:attr/listPreferredItemHeightSmall"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:textColor="@android:color/black"/>
</LinearLayout>複製程式碼

② queryData()

只是簡單的建立一個資料庫(music.db),庫中有一張tb_music表,表中有_id和name兩個欄位,然後填充資料,查詢資料,相對比較簡單,這裡就不做過多解釋了。

private Cursor queryData(String key) {
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(getFilesDir() + "music.db", null);
    Cursor cursor = null;
    try {
        String querySql = "select * from tb_music where name like '%" + key + "%'";
        cursor = db.rawQuery(querySql, null);
    } catch (Exception e) {
        e.printStackTrace();
        String createSql = "create table tb_music (_id integer primary key autoincrement,name varchar(100))";
        db.execSQL(createSql);

        String insertSql = "insert into tb_music values (null,?)";
        for (int i = 0; i < Cheeses.sCheeseStrings.length; i++) {
            db.execSQL(insertSql, new String[]{Cheeses.sCheeseStrings[i]});
        }

        String querySql = "select * from tb_music where name like '%" + key + "%'";
        cursor = db.rawQuery(querySql, null);
    }
    return cursor;
}複製程式碼

2)結合ListView實現搜尋提示

雖然上面已經實現了搜尋提示的功能,但網易雲音樂本地搜尋出來的結果並不是彈出式的,而是在SearchView下方以列表的方式呈現,要做到這樣的效果,就必需讓SearchView結合ListView一起使用。其實這並不難,因為AutoCompleteTextView設定的介面卡跟ListView要設定的介面卡是一樣的,直接將上邊的介面卡設定給ListView即可。

// 監聽搜尋框文字變化
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String s) {
        return false;
    }

    @Override
    public boolean onQueryTextChange(String s) {
        Cursor cursor = TextUtils.isEmpty(s) ? null : queryData(s);
        // 設定或更新ListView的介面卡
        setAdapter(cursor);
        return false;
    }
});

private void setAdapter(Cursor cursor) {
    if (mLv.getAdapter() == null) {
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(SearchViewActivity2.this, R.layout.item_layout, cursor, new String[]{"name"}, new int[]{R.id.text1});
        mLv.setAdapter(adapter);
    } else {
        ((SimpleCursorAdapter) mLv.getAdapter()).changeCursor(cursor);
    }
}複製程式碼

這樣就完成了,雖然樣式上是醜了點,但,那又怎樣,呵呵~

MaterialDesign 之 SearchView 全面解鎖

最後附上Demo連結

github.com/GitLqr/Mate…

相關文章