MaterialDesign 之 SearchView 全面解锁

DennisHa 7年前
   <h2>一、简述</h2>    <p>SearchView是v7包中的一个兼容性控件,它可以单独使用,也可以配合menu+toolbar一起使用。本文将使用第二种方式,对SearchView进行探索。最后将通过代码实战,实现 “仿网易云音乐本地音乐搜索” 效果,带你全面解锁SearchView的UI定制及查询功能的实现。</p>    <h2>二、常规使用</h2>    <p>*本文重点是SearchView,所以对Toolbar的使用及注意事项在本文中将不会有过多的体现,如需了解可自行百度或直接查看本Demo源码(源码在文末)。</p>    <h2>1、在menu的xml文件中配置</h2>    <p>要跟menu一起使用,就必须在menu的xml文件中对其中的一个item进行actionViewClass属性配置,如:</p>    <pre>  <code class="language-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"        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></code></pre>    <p>这个item跟普通item的差别在于使用了app:actionViewClass属性(注意是app:开头的!!!),这里使用的是兼容性控件里的SearchView,所以取值"android.support.v7.widget.SearchView"。其中title的设置不会生效(一般设置了title的item,长按后会有弹出提示文字),这里去掉也无所谓。</p>    <h2>2、在onCreateOptionsMenu()中得到SearchView</h2>    <p>我们知道menu在创建时会回调Activity中的onCreateOptionsMenu(Menu menu)方法,通过该方法可以得到Menu对象,而SearchView是Menu中item的一个actionView,actionView可以通过MenuItemCompat获取,故,通过Menu对象可以得到SearchView。</p>    <pre>  <code class="language-java">@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);  }</code></pre>    <p>到这里就可以看到效果了:</p>    <p><img src="https://simg.open-open.com/show/f22cbe54fb2cb5e499335fb69fca1e08.gif"></p>    <h2>3、对SearchView进行设置</h2>    <h3>默认展开搜索框</h3>    <p>手淘的首页搜索框是默认展开的,使用SearchView可以做一样的效果。此外,SearchView有三种默认展开的设置,效果上有略微不同,请结合注释与图片仔细观察。</p>    <pre>  <code class="language-java">/*------------------ SearchView有三种默认展开搜索框的设置方式,区别如下: ------------------*/  //设置搜索框直接展开显示。左侧有放大镜(在搜索框中) 右侧有叉叉 可以关闭搜索框  mSearchView.setIconified(false);  //设置搜索框直接展开显示。左侧有放大镜(在搜索框外) 右侧无叉叉 有输入内容后有叉叉 不能关闭搜索框  mSearchView.setIconifiedByDefault(false);  //设置搜索框直接展开显示。左侧有无放大镜(在搜索框中) 右侧无叉叉 有输入内容后有叉叉 不能关闭搜索框  mSearchView.onActionViewExpanded();</code></pre>    <p>按顺序效果依次如下:</p>    <p><img src="https://simg.open-open.com/show/6fe421a4656d52c55da7a2b9aa6c56a9.gif"></p>    <p>setIconified(false)</p>    <p><img src="https://simg.open-open.com/show/77994260b6a159058f683fcda3b482a6.gif"></p>    <p>setIconifiedByDefault(false)</p>    <p><img src="https://simg.open-open.com/show/8963fcdd6dc2fa55cfec7b0758fe3424.gif"></p>    <p>onActionViewExpanded()</p>    <h3>SearchView的常规设置</h3>    <pre>  <code class="language-java">//设置最大宽度  mSearchView.setMaxWidth(500);  //设置是否显示搜索框展开时的提交按钮  mSearchView.setSubmitButtonEnabled(true);  //设置输入框提示语  mSearchView.setQueryHint("hint");</code></pre>    <p>比较容易,直接看下效果:</p>    <p><img src="https://simg.open-open.com/show/e3ce25ff562138c3a4143737092dc7b7.gif"></p>    <h3>SearchView的事件监听</h3>    <p>SearchView提供的事件监听还是比较丰富的,一般常用的有打开搜索框按钮的点击事件、清空或关闭搜索框按钮的点击事件、搜索框文字变化事件等。</p>    <pre>  <code class="language-java">//搜索框展开时后面叉叉按钮的点击事件  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;      }  });</code></pre>    <p>这个也比较容易,直接看下效果:</p>    <p><img src="https://simg.open-open.com/show/0c58e316a0f3ca4bfd18a836ec10757a.gif"></p>    <p>以上就是SearchView给开发者提供的常规方法调用和属性设置,但是这并不能满足我们的开发需求,因为开发中大部分设计师根据不管MaterialDesign的设计规范,所以大多数情况下需要根据UI设计稿自定义SearchView的样式了,这相对比较复杂,下面将通过实战来学习SearchView的样式自定义,以此来满足我们的开发需求。</p>    <h2>三、实战</h2>    <p>仿网易云音乐本地音乐搜索,先看下效果,然后开始实战:</p>    <p><img src="https://simg.open-open.com/show/2217ae7d7e4c3c746742dd7017f0ca10.gif"></p>    <h2>1、设置Toolbar</h2>    <h3>1)创建该界面的布局activity_search_view2.xml</h3>    <p>指定Toolbar的高度、NaviagtionIcon、标题、字体等</p>    <pre>  <code class="language-java"><?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></code></pre>    <p>其中style指向的Toolbar.MyStyle是设置标题与NavigationIcon的距离,titleTextAppearance指向的Toolbar.TitleText是设置标题文字大小。</p>    <p>在style.xml中创建Toolbar的自定义样式</p>    <pre>  <code class="language-java"><!--标题与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></code></pre>    <p>如果不设置的话,效果不好,NavigationIcon和Toolbar的标题之前的间距看起来很大,下面看下设置前后的差别:</p>    <p><img src="https://simg.open-open.com/show/9077946fc68e33d6aadf9ca02e232998.png"></p>    <p><img src="https://simg.open-open.com/show/8d2c293bb52aedcb5f5d481bba00552a.png"></p>    <h3>2)设置去除ActionBar的主题</h3>    <p>在Style.xml中创建无ActionBar的主题,并设置主题背景色</p>    <pre>  <code class="language-java"><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></code></pre>    <p>不设置textColorSecondary的话,默认menu的item图标是黑色,下面看下设置前后的差别:</p>    <p><img src="https://simg.open-open.com/show/78c0d46eb63d12552c37fbe289137418.png"></p>    <p><img src="https://simg.open-open.com/show/8d2c293bb52aedcb5f5d481bba00552a.png"></p>    <p>为Activity设置主题</p>    <pre>  <code class="language-java"><?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></code></pre>    <h3>3)在Activity中设置Toolbar的代码如下:</h3>    <pre>  <code class="language-java">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);      }      ...  }</code></pre>    <h2>2、设置Menu</h2>    <h3>1)创建菜单布局search_view.xml</h3>    <p>跟之前的代码相比,只是多加了几个item而已。</p>    <pre>  <code class="language-java"><?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></code></pre>    <h3>2)在Activity中设置Menu的代码如下:</h3>    <pre>  <code class="language-java">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);      }  }</code></pre>    <p>到这里,除了搜索框(SearchView)以外,整个布局的效果大体上都实现了:</p>    <p><img src="https://simg.open-open.com/show/c47be4b42bc3c02bee117471b7d8f1ad.gif"></p>    <h2>3、定制SearchView样式</h2>    <p>接下来要实现的样式自定义有:</p>    <p><img src="https://simg.open-open.com/show/1f01e3a9079b7a1c2098c4d4b33f96bd.png"></p>    <p>重点来了,我们先来分析一下。SearchView本身不向外提供 “关闭搜索框” 和 “设置搜索框左边的搜索图标” 等方法,所以需要通过其他的方式来实现样式自定义。</p>    <h3>考虑:</h3>    <p>如果SearchView的布局结构是通过xml布局文件来实现的,那么可以通过SearchView.findViewById()的方式得到其中的部分或所有的控件;如果是通过代码动态添加的话,那么可以通过反射的方式得到我们需要的控件,进而对控件进行样式设置。</p>    <h3>结论:</h3>    <p>实现证明,SearchView的布局结构就是使用xml布局文件实现的,该xml文件名为abc_search_view.xml,且基本上每个控件都有id,这样就可以拿到需要的控件来实现样式自定义了。</p>    <h3>1)点击返回按钮,退出搜索框(若搜索框显示的话)</h3>    <p>SearchView本身没有提供关闭搜索框的方法(反正我是没找到啊~~),不过SearchView中正好有一个onCloseClicked()方法是用来关闭搜索框,我们可以通过反射来调用该方法,先来理解下该方法都做了什么,onCloseClicked()的代码如下:</p>    <pre>  <code class="language-java">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);      }  }</code></pre>    <p>这里要考虑到,当搜索框显示时,按下Toolbar的返回按钮关闭搜索框,否则就关闭当前界面。因为搜索框也有id,所以我们可以通过id可以得到搜索框控件,用来判断当前搜索框的显隐状态。结合SearchView内部的onCloseClicked()方法,最后Toolbar返回按钮的点击事件代码可以这么写:</p>    <pre>  <code class="language-java">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);      }        ...  }</code></pre>    <p><img src="https://simg.open-open.com/show/b4e1c721824ee640b9f3395c5f5372e2.gif"></p>    <h3>2)隐藏搜索框左边的搜索图标</h3>    <p>搜索框中左边的搜索图标不是一个控件,所以没办法通过id得到,但好在可以通过设置style的方式来修改SearchView所有的图标。方法也很简单,只需创建一个style(这里取名Widget.SearchView)继承自Widget.AppCompat.SearchView,然后替换需要修改的属性即可。先看下Widget.AppCompat.SearchView的父级Base.Widget.AppCompat.SearchView吧:</p>    <p><img src="https://simg.open-open.com/show/1c51adad20956cf21a36b9b6dbfab84d.png"></p>    <p>可以看到,这个父级style提供了SearchView中几乎所有的Icon属性,这意味着在图标定制上可以有很大拓展性。其中,layout是指定SearchView的布局,原始布局就是abc_search_view.xml,我们一般不会去动这个属性。</p>    <p>这里我们只需要去掉搜索框左边的图标(即:searchHintIcon),直接设置为@null就好了,如下修改style文件中的Widget.SearchView主题:</p>    <pre>  <code class="language-java"><!--没有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></code></pre>    <p><img src="https://simg.open-open.com/show/eed058cfee94ec21ee88f097af11ce5c.png"></p>    <h3>3)设置搜索框的提示文字</h3>    <p>修改提示文字内容</p>    <p>修改搜索框提示文字的方式有两种,一种就是修改SearchView的style,如上一步中,修改Widget.AppCompat.SearchView的defaultQueryHint属性;另一种方式是调用SearchView的setQueryHint()来修改。这两种方式都可以,如果同时用这两种方式来设置搜索框的提示语,则最终的提示内容将以代码设置方式为主。</p>    <pre>  <code class="language-java">@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");    }</code></pre>    <p><img src="https://simg.open-open.com/show/d046e68ca6f7b32d43ad23c07a6bc10b.png"></p>    <p>修改提示文字样式</p>    <p>SearchView也没有提供任何直接修改搜索框提示文字样式的方法,但既然我们可以通过id得到搜索框控件,那设置提示文字的样式便不是什么问题了,代码如下:</p>    <pre>  <code class="language-java">@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);  }</code></pre>    <p><img src="https://simg.open-open.com/show/32b38b54d38ecf8071d091897f14d9d5.png"></p>    <h3>4)根据搜索框中有无文字,来显隐搜索框右边的叉叉</h3>    <p>这个有点像searchView.onActionViewExpanded()的效果,唯一的区别就是搜索框不能是默认展开的,这要怎么办呢?通过观察onActionViewExpanded()的源码,可以发现该方法中调用了setIconified(false)!!!再联想到setIconified(false)本身就有让搜索框默认展开的效果,这是不是意味着,只要让onActionViewExpanded()的setIconified(false)改为setIconified(true)就好了呢?答案是是的。而且不需要重写SearchView,因为onActionViewExpanded()和setIconified(true)是可以搭配使用的,只要依次调用这两个方法就可以实现这种效果了,代码如下:</p>    <pre>  <code class="language-java">@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);  }</code></pre>    <p><img src="https://simg.open-open.com/show/3477344ba4218cd2ff9cbbf139574f31.gif"></p>    <h2>4、实现搜索提示功能</h2>    <p>上面我们已经学习了SearchView的UI定制,下面将通过SearchView自身或结合ListView的方式(RecyclerView应该也一样吧,还没试过)直接学习SearchView搜索提示功能的实现,继续完善 “仿网易云音乐本地音乐搜索” 效果。</p>    <h3>1)弹出式搜索提示</h3>    <p>SearchView本身的搜索框就是AutoCompleteTextView的一个子类,有图有真相。</p>    <p><img src="https://simg.open-open.com/show/26440460a906bd630000f70eba074345.png"></p>    <p><img src="https://simg.open-open.com/show/5ab604c0c6c67e40d525cdd8d2d51ec2.png"></p>    <p>AutoCompleteTextView是可以通过设置适配器来实现文本补全提示功能的,所以,SearchView中的搜索框一样也可以,不过SearchView提供了setSuggestionsAdapter()方法可以直接为搜索框设置适配器,需要注意的是,这个适配器必须跟数据库的Cursor对象一起使用,例如:</p>    <pre>  <code class="language-java">mSearchView.setSuggestionsAdapter(new SimpleCursorAdapter(SearchViewActivity2.this, R.layout.item_layout, cursor, new String[]{"name"}, new int[]{R.id.text1}));</code></pre>    <p>一般开发中遇到的需求是一边输入关键字一边显示搜索结果,所以需要监听搜索框的文字输入,一旦文字变化就查询数据库,更新搜索结果,所以代码可以这么写:</p>    <pre>  <code class="language-java">// 监听搜索框文字变化  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;      }  });</code></pre>    <p>对于SimpleCursorAdapter的使用,不熟悉的自己百度学习吧,下面看效果:</p>    <p><img src="https://simg.open-open.com/show/4bb2833dc4b9917a058de9badd145461.gif"></p>    <p>可以发现,当输入第一个文字"a"时,没有什么反应,当输入第二个文字"a"时,弹出了一个列表弹窗,这是由于AutoCompleteTextView本身默认触发查询动作的条件就是该控件中的文字至少要2个以上,如果我们想修改成只要有一个文字就触发查询的话,则可以这么做:</p>    <ol>     <li>拿到SearchView中搜索框控件</li>     <li>调用setThreshold()设置触发查询的字数</li>    </ol>    <p>直接上代码:</p>    <pre>  <code class="language-java">@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);  }</code></pre>    <p>再看下效果:</p>    <p><img src="https://simg.open-open.com/show/ce93b9bbe81c3270c2794614d69ef7ff.gif"></p>    <p>好了,弹出式搜索功能做完了,下面贴出条目布局item_layout.xml和queryData()方法的代码实现:</p>    <p>① item_layout.xml</p>    <pre>  <code class="language-java"><?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></code></pre>    <p>② queryData()</p>    <p>只是简单的创建一个数据库(music.db),库中有一张tb_music表,表中有_id和name两个字段,然后填充数据,查询数据,相对比较简单,这里就不做过多解释了。</p>    <pre>  <code class="language-java">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;  }</code></pre>    <h3>2)结合ListView实现搜索提示</h3>    <p>虽然上面已经实现了搜索提示的功能,但网易云音乐本地搜索出来的结果并不是弹出式的,而是在SearchView下方以列表的方式呈现,要做到这样的效果,就必需让SearchView结合ListView一起使用。其实这并不难,因为AutoCompleteTextView设置的适配器跟ListView要设置的适配器是一样的,直接将上边的适配器设置给ListView即可。</p>    <pre>  <code class="language-java">// 监听搜索框文字变化  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);      }  }</code></pre>    <p>这样就完成了,虽然样式上是丑了点,但,那又怎样,呵呵~</p>    <p><img src="https://simg.open-open.com/show/8a3d4287f802eff220d449b61b0d4327.gif"></p>    <h2>最后附上Demo链接</h2>    <p><a href="/misc/goto?guid=4959748917937018287" rel="nofollow,noindex">https://github.com/GitLqr/MaterialDesignDemo</a></p>    <p> </p>