1. 程式人生 > >MaterialDesign學習篇(五),使用SearchView的正確姿勢

MaterialDesign學習篇(五),使用SearchView的正確姿勢

介紹

大多APP都具有搜尋功能,但是大部分都是在標題欄中放置搜尋的圖示或者是不可輸入的EditText,當點選的時候,開啟另外一個介面進行搜尋,但是網易雲音樂在搜尋本地音樂的時候,點選搜尋按鈕,就會出現輸入框,點選返回時,又會再次收起,以前認為需要自己根據狀態做佈局的改變,最後發現原來有一個很方便好用的控制元件,叫做SearchView,現在開始學習下如何使用SearchView。

網易雲音樂的效果如下:

使用SearchView

SearchView需要和Toolbar一起使用,如果不熟悉Toolbar的使用,可以檢視我寫過的介紹Toolbar的文章:

建立帶SearchView的menu

在res->menu資料夾中我們新建一個叫做menu_search_view.xml的檔案,檔案的內容為:

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

    <!--右側搜尋操作條目-->
    <item
        android:id="@+id/action_search"
        android:title="@string/menu_search"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="always"/>

    <!--右側設定條目,收起-->
    <item
        android:id="@+id/action_setting"
        android:title="@string/menu_setting"
        app:showAsAction="never" />

</menu>

其中第一個是搜尋的條目,我們指定了它的actionViewClass=”android.support.v7.widget.SearchView”,這就表明它是一個SearchView,和其他的選單條目不同,下面會給大家演示它的不同之處。

佈局檔案中使用Toolbar

<?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"
        android:background="@color/netease_red"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:title="我的音樂"
        app:titleTextColor="@android:color/white"
        />

</LinearLayout>

Activity中展示Toolbar和Menu

找到Toolbar物件,呼叫setSupportActionBar()方法,重寫onCreateOptionsMenu()方法展示右側選單項

public class SearchViewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_search_view);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_search_view, menu);

        //找到searchView
        MenuItem searchItem = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);

        return super.onCreateOptionsMenu(menu);
    }
}

效果如圖:

可以看到,右側放置有搜尋的圖示和豎立的三點水(更多),搜尋的圖示並沒有指定,是系統自帶的,由於指定了actionViewClass屬性為android.support.v7.widget.SearchView,所以該選單項具有搜尋的功能,點選後會展現出搜尋框,如果輸入框中有內容,當點選搜尋框右側的關閉時,會清除文字框中的內容,如果輸入框中沒有內容,點選關閉圖示時,它會收起來,變成原來的搜尋圖示。

更改預設圖示的顏色

上圖中圖示的顏色是黑色的,看起來是不是有些不堪入目,是不是很想改變它的顏色,改變選單項圖示的顏色不難,只需要在Activity的主題中,指定Toolbar選單項圖示的顏色:

<style name="SeachViewActivityTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/netease_red</item>
    <item name="colorPrimaryDark">@color/netease_red</item>
    <item name="colorAccent">@color/netease_red</item>
    <!--toolbar選單項圖示的顏色-->
    <item name="android:textColorSecondary">@android:color/white</item>
</style>

只需要設定android:textColorSecondary,指定Toolbar中選單項圖示的顏色,我們現在把它指定為white,效果如下:

是不是覺得好看多了,頓時覺得介面清新了許多。

設定SearchView的展開樣式

一、讓SearchView一開始就處於展開狀態:

程式碼:

searchView.setIconified(false);//一開始處於展開狀態

可以看到SearchView一開始處於展開的狀態,而且關閉的圖示也顯示出來,當輸入框中有內容的時候,點選關閉的圖示是清除內容,當輸入框中沒有內容的時候,點選關閉圖示是收起張開狀態。

二、設定SearchView無法收起

程式碼:

searchView.setIconified(false);//設定searchView處於展開狀態
searchView.onActionViewExpanded();// 當展開無輸入內容的時候,沒有關閉的圖示

可以看到SearchView一開始處於展開的狀態,但是關閉的圖示一開始沒有顯示出來,所以無法收起展開狀態,當輸入框中有內容的時候,點選關閉的圖示是清除內容。

三、設定搜尋小圖示的位置在框外

程式碼:

searchView.setIconified(false);//設定searchView處於展開狀態
searchView.onActionViewExpanded();// 當展開無輸入內容的時候,沒有關閉的圖示
searchView.setIconifiedByDefault(false);//預設為true在框內,設定false則在框外

可以看到,第三張圖的效果和第二張圖的差不多,只不過搜尋的小圖示是在輸入框外而不是輸入框內。

SearchView的監聽

內容變化以及點選提交的監聽

程式碼:

    searchView.setSubmitButtonEnabled(true);//顯示提交按鈕
    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            //提交按鈕的點選事件
            Toast.makeText(SearchViewActivity.this, query, Toast.LENGTH_SHORT).show();
            return true;
        }

        @Override
        public boolean onQueryTextChange(String newText) {
            //當輸入框內容改變的時候回撥
            Log.i(TAG,"內容: " + newText);
            return true;
        }
    });

log截圖:

上圖中可以看到,輸入框的右側有一個”>”圖示,即提交按鈕,按鈕的點選事件在onQueryTextSubmit()方法中回撥處理,這裡我只是簡單彈出搜尋框中的內容,當輸入框中的內容變化時,會在onQueryTextChange()方法中回撥,這裡我將變化的內容列印在控制檯。

設定無內容時的提示文字

searchView.setQueryHint("輸入歌曲名查詢");//設定預設無內容時的文字提示

修改搜尋圖示

想要修改預設的搜尋圖示,需要在Activity的主題中修改,像剛才修改預設圖示的顏色一樣,需要在style.xml中對應Activity的主題中進行修改:

<style name="SeachViewActivityTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/netease_red</item>
    <item name="colorPrimaryDark">@color/netease_red</item>
    <item name="colorAccent">@color/netease_red</item>
    <!--toolbar選單項圖示的顏色-->
    <item name="android:textColorSecondary">@android:color/white</item>
    <!--修改searchView樣式-->
    <item name="searchViewStyle">@style/SearchViewStyle</item>
</style>

<style name="SearchViewStyle" parent="Widget.AppCompat.SearchView">
    <item name="searchIcon">@mipmap/ic_search</item>
</style>

可以看到搜尋圖示變成自己指定的圖示了,無論是展開還是收起狀態,圖示都改變了.

修改輸入框文字提示的顏色和內容文字的顏色

 mSearchAutoComplete = (SearchView.SearchAutoComplete) mSearchView.findViewById(R.id.search_src_text);

 //設定輸入框提示文字樣式
 mSearchAutoComplete.setHintTextColor(getResources().getColor(android.R.color.white));//設定提示文字顏色
 mSearchAutoComplete.setTextColor(getResources().getColor(android.R.color.white));//設定內容文字顏色

可以看到,提示文字的顏色和內容文字的顏色都變成白色了。

去除搜尋框中的圖示

<style name="SeachViewActivityTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/netease_red</item>
    <item name="colorPrimaryDark">@color/netease_red</item>
    <item name="colorAccent">@color/netease_red</item>
    <!--toolbar選單項圖示的顏色-->
    <item name="android:textColorSecondary">@android:color/white</item>
    <!--修改searchView樣式-->
    <item name="searchViewStyle">@style/SearchViewStyle</item>
</style>

<style name="SearchViewStyle" parent="Widget.AppCompat.SearchView">
    <item name="searchIcon">@mipmap/ic_search</item>
    <item name="searchHintIcon">@null</item>
</style>

指定SearchView樣式中的searchHintIcon為null,即可去除搜尋框中的圖示,如圖:

關聯navigationIcon的點選

這裡我們嘗試做成跟網易雲音樂一樣,當搜尋框處於展開的時候,點選返回鍵是收起SearchView,當SearchView處於收起時,點選是關閉當前Activity。

修改佈局檔案中的Toolbar,指定navigationIcon:

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:background="@color/netease_red"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:title="本地音樂"
    app:titleTextColor="@android:color/white"
    app:navigationIcon="@mipmap/back"
    />

Activity中,設定navigationIcon的點選事件:

 toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mSearchAutoComplete.isShown()) {
                try {
                    mSearchAutoComplete.setText("");//清除文字
                    //利用反射呼叫收起SearchView的onCloseClicked()方法
                    Method method = mSearchView.getClass().getDeclaredMethod("onCloseClicked");
                    method.setAccessible(true);
                    method.invoke(mSearchView);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                finish();
            }
        }
    });

設定navigationIcon的點選事件時,我們判斷當前SearchView是否處於顯示狀態,通過呼叫SearchAutoComplete的isShown()方法,如果處於顯示狀態,則清除輸入框中的內容,接著通過反射呼叫SearchView的onCloseClicked()方法,收起SearchView;如果SearchView處於收起狀態,點選則是關閉當前Activity.現在是不是覺得很接近網易雲音樂的搜尋本地音樂的樣子了。

修改navigationIcon和SearchView之間的距離

上圖中navigationIcon和SearchView之間的距離過大,我們對其進行修改,需要在style.xml定義Toolbar的樣式:

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

在佈局檔案中,引用Toolbar的樣式:

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    style="@style/ToolbarStyle"
    android:background="@color/netease_red"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:title="本地音樂"
    app:titleTextColor="@android:color/white"
    app:navigationIcon="@mipmap/back"
    />

Toolbar的樣式中,我們修改了navigationIcon和title之間的距離,將其修改為0,現在看起來navigationIcon和SearchView之間的距離是不是變小了,好看些了。

模仿網易雲音樂搜尋本地音樂功能

先看下效果:

修改佈局檔案,即Toolbar + ListView:

<?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/ToolbarStyle"
    android:background="@color/netease_red"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:title="本地音樂"
    app:titleTextColor="@android:color/white"
    app:navigationIcon="@mipmap/back"
    />

<ListView
    android:id="@+id/lv_music"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

</LinearLayout>

當搜尋內容變化的時候,呼叫模糊檢視本地音樂的功能:

mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            //提交按鈕的點選事件
            Toast.makeText(SearchViewActivity.this, query, Toast.LENGTH_SHORT).show();
            return true;
        }

        @Override
        public boolean onQueryTextChange(String newText) {
            //當輸入框內容改變的時候回撥
            // Log.i(TAG,"內容: " + newText);
            quertMusic(newText);
            return true;
        }
    });

 /**
 * 模糊查詢音樂
 * @param key
 */
private void quertMusic(String key) {
    String[] musics = new String[]{};
    if (!TextUtils.isEmpty(key)){
        musics = FileUtils.queryMusic(this, key);
    }
    ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, musics);
    mLvMusic.setAdapter(adapter);
}

FileUtils中,查詢本地音樂資料庫的queryMusic()方法:

/**
 * 根據歌名檢視音樂
 * @param context 上下文
 * @param key 關鍵字
 * @return
 */
public static String[] queryMusic(Context context, String key) {
    ArrayList<String> nameList = new ArrayList<>();
    Cursor c = null;
    try {
        c = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,
                MediaStore.Audio.Media.DISPLAY_NAME + " LIKE '%" + key + "%'",
                null,
                MediaStore.Audio.Media.DEFAULT_SORT_ORDER);

        while (c.moveToNext()) {
            String path = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));// 路徑

            if (!FileUtils.isExists(path)) {
                continue;
            }

            String name = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME)); // 歌曲名
            nameList.add(name);
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (c != null) {
            c.close();
        }
    }
    if (nameList.isEmpty()){
        return new String[]{};
    }
    return (String[])nameList.toArray(new String[nameList.size()]);
}

利用ContentResolver查詢本地音樂資料庫,這裡只是簡單查詢出歌曲名,然後將歌曲的集合轉換成陣列,需要注意的是,如果是Android6.0以上,需要動態申請讀取SD卡的許可權:

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_search_view);

    initView();
    initToolbar();
    requestPermission();
}

private void requestPermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
        //如果還沒有讀取SD卡的許可權,申請
        ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},REQ_PERMISSION);
    }
}

這裡在初始化完畢的時候進行許可權的判斷和申請。

好的,到這裡SearchView的相關用法已經介紹完畢了,需要原始碼的可以檢視: