介绍
大多APP都具有搜索功能,但是大部分都是在标题栏中放置搜索的图标或者是不可输入的EditText,当点击的时候,开启另外一个界面进行搜索,但是网易云音乐在搜索本地音乐的时候,点击搜索按钮,就会出现输入框,点击返回时,又会再次收起,以前认为需要自己根据状态做布局的改变,最后发现原来有一个很方便好用的控件,叫做SearchView,现在开始学习下如何使用SearchView。
网易云音乐的效果如下:
使用SearchView
SearchView需要和Toolbar一起使用,如果不熟悉Toolbar的使用,可以查看我写过的介绍Toolbar的文章:
MaterialDesign学习篇(二),Toolbar、DrawerLayout的使用
创建带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的相关用法已经介绍完毕了,需要源码的可以查看: