Android自定义View实现搜索框(SearchView)功能

Android自定义View实现搜索框(SearchView)功能,第1张

概述概述在Android开发中,当系统数据项比较多时,常常会在app添加搜索功能,方便用户能快速获得需要的数据。搜索栏对于我们并不陌生,在许多app都能见到它,比如豌豆荚

概述

在AndroID开发中,当系统数据项比较多时,常常会在app添加搜索功能,方便用户能快速获得需要的数据。搜索栏对于我们并不陌生,在许多app都能见到它,比如豌豆荚

在某些情况下,我们希望我们的自动补全信息可以不只是纯文本,还可以像豌豆荚这样,能显示相应的图片和其他数据信息,因此AndroID给我们提供的autoCompleteTextVIEw往往就不够用,在大多情况下我们都需要自己去实现搜索框。

分析

根据上面这张图,简单分析一下自定义搜索框的结构与功能,有
1. 搜索界面大致由三部门组成,如图:输入框+(自动补全)提示框+结果列表。
2. 提示框的数据与输入框输入的文本是实时联动的,而结果列表只有在每次进行搜索 *** 作时才会更新数据

3. 输入框的UI应是动态的,即UI随着输入的文本的改变而改变,如:在未输入文本时,清除按钮

应该是隐藏的;只有当框中有文本时才会显示。
4. 软键盘也应该是动态的,如完成搜索时应自动隐藏。
5. 选择提示框的选项会自动补全输入框,且自动进行搜索
6. (external)有热门搜索推荐/记录搜索记录的功能――热门搜索推荐列表只在刚要进行搜索的时候d出,即未输入文本时,可供用户选择。

根据上面的分析,我们认为一个搜索框应该包含输入框和提示框两个部分。搜索框可以设置一个回调监听接口,当需要进行搜索 *** 作时,调用监听者的search()方法,从而实现具体的搜索 *** 作以及结果列表的数据联动。

演示Demo

注意:

1. 这里,博主图方便没有模拟太多数据,而且提示框和热搜列表也都只是使用String类型的数据,各位看官们可以根据自身需要去设置item_layout和相应的adapter。
2. 由于个人习惯,博主在这个demo中使用了通用适配器,所以生成和设置adapter的代码比较简略,看官们可以根据传统的VIEwHolder模式打造自己的adapter。或者学习一下通用适配器的打造。可以参考这里(鸿神博客Again)学习一下通用适配器的打造,在我的源码里面也有对应的源码。

实现

好了,说了那么多,开始来看代码吧

先看SearchVIEw的布局文件 search_layout.xml

<?xml version="1.0" enCoding="utf-8"?> <linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID"        androID:background="#eee"        androID:layout_wIDth="match_parent"        androID:layout_height="wrap_content"        androID:orIEntation="vertical">    <linearLayout     androID:background="#eb4f38"     androID:layout_wIDth="match_parent"     androID:layout_height="wrap_content"     androID:orIEntation="horizontal">       <FrameLayout        androID:layout_weight="1"       androID:layout_wIDth="0dp"       androID:layout_height="wrap_content">        <EditText         androID:ID="@+ID/search_et_input"         androID:layout_gravity="center_vertical"         androID:layout_margin="10dp"         androID:drawableleft="@drawable/search_icon"         androID:drawablepadding="5dp"         androID:layout_wIDth="match_parent"         androID:layout_height="wrap_content"         androID:background="@drawable/search_edittext_shape"         androID:textSize="16sp"         androID:imeOptions="actionSearch"         androID:inputType="text"         androID:hint="请输入关键字"/>        <ImageVIEw         androID:visibility="gone"         androID:layout_marginRight="20dp"         androID:src="@drawable/iv_delete_bg"         androID:ID="@+ID/search_iv_delete"         androID:layout_gravity="right|center_vertical"         androID:layout_wIDth="wrap_content"         androID:layout_height="wrap_content"/>     </FrameLayout>      <button       androID:ID="@+ID/search_btn_back"       androID:layout_marginRight="10dp"       androID:layout_margintop="10dp"       androID:layout_marginBottom="10dp"       androID:layout_gravity="center_vertical"       androID:background="@drawable/btn_search_bg"       androID:layout_wIDth="@dimen/btn_wIDth"       androID:layout_height="@dimen/btn_height"       androID:text="返回"       androID:textcolor="@color/color_white"/>   </linearLayout>    <ListVIEw     androID:visibility="gone"     androID:ID="@+ID/search_lv_tips"     androID:background="@drawable/lv_search_tips_bg"     androID:layout_marginleft="20dp"     androID:layout_marginRight="20dp"     androID:layout_marginBottom="10dp"     androID:layout_wIDth="match_parent"     androID:layout_height="200dp">   </ListVIEw> </linearLayout> 

注意:demo中颜色什么的都直接用的rgb 值去设置,在实际开发时,需要把它们都统一管理到values目录下 。

比较简单,需要注意的是EditText的这个属性

androID:imeOptions="actionSearch"
就是把Enter键设置为Search键,并把点击Enter键的动作设为actionSearch,这样既可在代码中监听何时按下search键

没什么说的,bg属性可以直接看看源码。接下来看模拟的bean类,这里直接就叫Bean.java

public class Bean {    private int iconID;   private String Title;   private String content;   private String comments;    public Bean(int iconID,String Title,String content,String comments) {     this.iconID = iconID;     this.Title = Title;     this.content = content;     this.comments = comments;   }    public int getIconID() {     return iconID;   }    public voID setIconID(int iconID) {     this.iconID = iconID;   }    public String getTitle() {     return Title;   }    public voID setTitle(String Title) {     this.Title = Title;   }    public String getContent() {     return content;   }    public voID setContent(String content) {     this.content = content;   }    public String getComments() {     return comments;   }    public voID setComments(String comments) {     this.comments = comments;   } } 

接着看主角SearchVIEw.java

public class SearchVIEw extends linearLayout implements VIEw.OnClickListener {    /**    * 输入框    */   private EditText etinput;    /**    * 删除键    */   private ImageVIEw ivDelete;    /**    * 返回按钮    */   private button btnBack;    /**    * 上下文对象    */   private Context mContext;    /**    * d出列表    */   private ListVIEw lvTips;    /**    * 提示adapter (推荐adapter)    */   private ArrayAdapter<String> mHintAdapter;    /**    * 自动补全adapter 只显示名字    */   private ArrayAdapter<String> mautoCompleteAdapter;    /**    * 搜索回调接口    */   private SearchVIEwListener mListener;    /**    * 设置搜索回调接口    *    * @param Listener 监听者    */   public voID setSearchVIEwListener(SearchVIEwListener Listener) {     mListener = Listener;   }    public SearchVIEw(Context context,AttributeSet attrs) {     super(context,attrs);     mContext = context;     LayoutInflater.from(context).inflate(R.layout.search_layout,this);     initVIEws();   }    private voID initVIEws() {     etinput = (EditText) findVIEwByID(R.ID.search_et_input);     ivDelete = (ImageVIEw) findVIEwByID(R.ID.search_iv_delete);     btnBack = (button) findVIEwByID(R.ID.search_btn_back);     lvTips = (ListVIEw) findVIEwByID(R.ID.search_lv_tips);      lvTips.setonItemClickListener(new AdapterVIEw.OnItemClickListener() {       @OverrIDe       public voID onItemClick(AdapterVIEw<?> adapterVIEw,VIEw vIEw,int i,long l) {         //set edit text         String text = lvTips.getAdapter().getItem(i).toString();         etinput.setText(text);         etinput.setSelection(text.length());         //hint List vIEw gone and result List vIEw show         lvTips.setVisibility(VIEw.GONE);         notifyStartSearching(text);       }     });      ivDelete.setonClickListener(this);     btnBack.setonClickListener(this);      etinput.addTextChangedListener(new EditChangedListener());     etinput.setonClickListener(this);     etinput.setonEditorActionListener(new TextVIEw.OnEditorActionListener() {       @OverrIDe       public boolean onEditorAction(TextVIEw textVIEw,int actionID,KeyEvent keyEvent) {         if (actionID == EditorInfo.IME_ACTION_SEARCH) {           lvTips.setVisibility(GONE);           notifyStartSearching(etinput.getText().toString());         }         return true;       }     });   }    /**    * 通知监听者 进行搜索 *** 作    * @param text    */   private voID notifyStartSearching(String text){     if (mListener != null) {       mListener.onSearch(etinput.getText().toString());     }     //隐藏软键盘     inputMethodManager imm = (inputMethodManager) mContext.getSystemService(Context.input_METHOD_SERVICE);     imm.toggleSoftinput(0,inputMethodManager.HIDE_NOT_ALWAYS);   }    /**    * 设置热搜版提示 adapter    */   public voID setTipsHintAdapter(ArrayAdapter<String> adapter) {     this.mHintAdapter = adapter;     if (lvTips.getAdapter() == null) {       lvTips.setAdapter(mHintAdapter);     }   }    /**    * 设置自动补全adapter    */   public voID setautoCompleteAdapter(ArrayAdapter<String> adapter) {     this.mautoCompleteAdapter = adapter;   }    private class EditChangedListener implements TextWatcher {     @OverrIDe     public voID beforeTextChanged(CharSequence charSequence,int i2,int i3) {      }      @OverrIDe     public voID onTextChanged(CharSequence charSequence,int i3) {       if (!"".equals(charSequence.toString())) {         ivDelete.setVisibility(VISIBLE);         lvTips.setVisibility(VISIBLE);         if (mautoCompleteAdapter != null && lvTips.getAdapter() != mautoCompleteAdapter) {           lvTips.setAdapter(mautoCompleteAdapter);         }         //更新autoComplete数据         if (mListener != null) {           mListener.onRefreshautoComplete(charSequence + "");         }       } else {         ivDelete.setVisibility(GONE);         if (mHintAdapter != null) {           lvTips.setAdapter(mHintAdapter);         }         lvTips.setVisibility(GONE);       }      }      @OverrIDe     public voID afterTextChanged(Editable editable) {     }   }    @OverrIDe   public voID onClick(VIEw vIEw) {     switch (vIEw.getID()) {       case R.ID.search_et_input:         lvTips.setVisibility(VISIBLE);         break;       case R.ID.search_iv_delete:         etinput.setText("");         ivDelete.setVisibility(GONE);         break;       case R.ID.search_btn_back:         ((Activity) mContext).finish();         break;     }   }    /**    * search vIEw回调方法    */   public interface SearchVIEwListener {      /**      * 更新自动补全内容      *      * @param text 传入补全后的文本      */     voID onRefreshautoComplete(String text);      /**      * 开始搜索      *      * @param text 传入输入框的文本      */     voID onSearch(String text);  //    /** //     * 提示列表项点击时回调方法 (提示/自动补全) //     */ //    voID onTipsItemClick(String text);   }  } 

搜索框主要包含两个结构:输入栏+d出框(自动补全或热门搜素推荐)。

代码不多,实现很简单,主要是需要给EditText(输入框)设置点击监听和文本改变监听,有以下几点:
1. 当输入框没有文本时,点击输入框,显示热门搜索列表框。
2. 当输入框有文本时,点击输入框,应显示自动补全列表框。
3. 当输入框的文本发生改变时,需要更新自动补全列表框的数据。由于这些数据应该是在外部(调用者)中获得的,所以可以通过接口回调的形式,当需要更新时,通知监听者更新数据。
4. 当输入框的文本从空”“变换到非空时,即有字符时,界面应显示自动补全框,隐藏热门搜索框。
5. 当输入框的文本从非空变为空时,系统应隐藏自动补全框和热门搜索框。
6. 需要监听是否按下search键(enter),按下时通知监听者执行search *** 作

结合以上6点和在上文分析过的内容,就能很轻松地实现该vIEw。

之后来看看搜索界面的布局文activity_main.xml

<linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID"        xmlns:tools="http://schemas.androID.com/tools"        androID:layout_wIDth="match_parent"        androID:layout_height="match_parent"        tools:context=".MainActivity"        androID:orIEntation="vertical">    <com.yetwish.customsearchdemo.activity.wIDge.SearchVIEw     androID:ID="@+ID/main_search_layout"     androID:layout_wIDth="match_parent"     androID:layout_height="wrap_content">   </com.yetwish.customsearchdemo.activity.wIDge.SearchVIEw>    <ListVIEw     androID:visibility="gone"     androID:ID="@+ID/main_lv_search_results"     androID:layout_wIDth="match_parent"     androID:layout_height="wrap_content">    </ListVIEw> </linearLayout> 

就是一个SearchVIEw加上一个结果列表,这些我们在上文都分析过了,所以也没什么好说的。布局可根据自身需求去自定义。

最后就是搜索界面调用该vIEw  MainActiviy.java

public class MainActivity extends Activity implements SearchVIEw.SearchVIEwListener {    /**    * 搜索结果列表vIEw    */   private ListVIEw lvResults;    /**    * 搜索vIEw    */   private SearchVIEw searchVIEw;     /**    * 热搜框列表adapter    */   private ArrayAdapter<String> hintAdapter;    /**    * 自动补全列表adapter    */   private ArrayAdapter<String> autoCompleteAdapter;    /**    * 搜索结果列表adapter    */   private SearchAdapter resultAdapter;    /**    * 数据库数据,总数据    */   private List<Bean> dbData;    /**    * 热搜版数据    */   private List<String> hintData;    /**    * 搜索过程中自动补全数据    */   private List<String> autoCompleteData;    /**    * 搜索结果的数据    */   private List<Bean> resultData;    /**    * 默认提示框显示项的个数    */   private static int DEFAulT_HINT_SIZE = 4;    /**    * 提示框显示项的个数    */   private static int hintSize = DEFAulT_HINT_SIZE;    /**    * 设置提示框显示项的个数    *    * @param hintSize 提示框显示个数    */   public static voID setHintSize(int hintSize) {     MainActivity.hintSize = hintSize;   }     @OverrIDe   protected voID onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     requestwindowFeature(Window.FEATURE_NO_Title);     setContentVIEw(R.layout.activity_main);     initData();     initVIEws();   }    /**    * 初始化视图    */   private voID initVIEws() {     lvResults = (ListVIEw) findVIEwByID(R.ID.main_lv_search_results);     searchVIEw = (SearchVIEw) findVIEwByID(R.ID.main_search_layout);     //设置监听     searchVIEw.setSearchVIEwListener(this);     //设置adapter     searchVIEw.setTipsHintAdapter(hintAdapter);     searchVIEw.setautoCompleteAdapter(autoCompleteAdapter);      lvResults.setonItemClickListener(new AdapterVIEw.OnItemClickListener() {       @OverrIDe       public voID onItemClick(AdapterVIEw<?> adapterVIEw,int position,long l) {         Toast.makeText(MainActivity.this,position + "",Toast.LENGTH_SHORT).show();       }     });   }    /**    * 初始化数据    */   private voID initData() {     //从数据库获取数据     getDbData();     //初始化热搜版数据     getHintData();     //初始化自动补全数据     getautoCompleteData(null);     //初始化搜索结果数据     getResultData(null);   }    /**    * 获取db 数据    */   private voID getDbData() {     int size = 100;     dbData = new ArrayList<>(size);     for (int i = 0; i < size; i++) {       dbData.add(new Bean(R.drawable.icon,"androID开发必备技能" + (i + 1),"AndroID自定义view――自定义搜索vIEw",i * 20 + 2 + ""));     }   }    /**    * 获取热搜版data 和adapter    */   private voID getHintData() {     hintData = new ArrayList<>(hintSize);     for (int i = 1; i <= hintSize; i++) {       hintData.add("热搜版" + i + ":AndroID自定义view");     }     hintAdapter = new ArrayAdapter<>(this,androID.R.layout.simple_List_item_1,hintData);   }    /**    * 获取自动补全data 和adapter    */   private voID getautoCompleteData(String text) {     if (autoCompleteData == null) {       //初始化       autoCompleteData = new ArrayList<>(hintSize);     } else {       // 根据text 获取auto data       autoCompleteData.clear();       for (int i = 0,count = 0; i < dbData.size()           && count < hintSize; i++) {         if (dbData.get(i).getTitle().contains(text.trim())) {           autoCompleteData.add(dbData.get(i).getTitle());           count++;         }       }     }     if (autoCompleteAdapter == null) {       autoCompleteAdapter = new ArrayAdapter<>(this,autoCompleteData);     } else {       autoCompleteAdapter.notifyDataSetChanged();     }   }    /**    * 获取搜索结果data和adapter    */   private voID getResultData(String text) {     if (resultData == null) {       // 初始化       resultData = new ArrayList<>();     } else {       resultData.clear();       for (int i = 0; i < dbData.size(); i++) {         if (dbData.get(i).getTitle().contains(text.trim())) {           resultData.add(dbData.get(i));         }       }     }     if (resultAdapter == null) {       resultAdapter = new SearchAdapter(this,resultData,R.layout.item_bean_List);     } else {       resultAdapter.notifyDataSetChanged();     }   }    /**    * 当搜索框 文本改变时 触发的回调,更新自动补全数据    * @param text    */   @OverrIDe   public voID onRefreshautoComplete(String text) {     //更新数据     getautoCompleteData(text);   }    /**    * 点击搜索键时edit text触发的回调    *    * @param text    */   @OverrIDe   public voID onSearch(String text) {     //更新result数据     getResultData(text);     lvResults.setVisibility(VIEw.VISIBLE);     //第一次获取结果 还未配置适配器     if (lvResults.getAdapter() == null) {       //获取搜索数据 设置适配器       lvResults.setAdapter(resultAdapter);     } else {       //更新搜索数据       resultAdapter.notifyDataSetChanged();     }     Toast.makeText(this,"完成搜素",Toast.LENGTH_SHORT).show();   }  } 

使用SearchVIEw比较简单,只要给SearchVIEw设置onSearchVIEwListener监听接口,实现对应的方法,并给SearchVIEw传入热搜版和自动补全的adapter既可。

这里使用的匹配算法比较简单,也没有考虑多个搜索词的情况,(这些之后都可以再完善),主要实现就是在总数据中匹配每个Bean的Title是否包含搜索词,包含则表示该数据匹配,否则不匹配。然后将所有匹配的Bean显示到结果列表中。

考虑到实际开发中,数据量十分庞大,可以只把结果集的一部分(如前10个)显示出来,上拉到底的时候再加载之后的记录,也就是可以加入上拉加载的机制,使app性能更优化。

自动补全匹配也是采用相同的算法。算法都比较简单,当然也可以弄得复杂点,比如根据“ ”(空格)去分割输入文本,再逐个考虑单个搜索词的匹配项,把匹配次数从多到少排列出结果集等等。这里不细说。

这里有一个问题是进入该搜索界面时需要加载所有的数据项到内存,当数据项很多时,是否会占用大量的内存?如果是应该如何避免?是采用只加载一部分数据的形式,还是直接使用搜索词到数据库中查询更优?还请各位看官大神们给出宝贵的意见~

好了,自定义搜索框到这就打造完成啦,是不是感觉简单过头了。

各位看官如果有任何问题可评论或者发邮件跟我联系[email protected]

~忘记贴代码了,代码放在github上,各位看官直接download即可
链接:https://github.com/yetwish/CustomSearchView

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

总结

以上是内存溢出为你收集整理的Android自定义View实现搜索框(SearchView)功能全部内容,希望文章能够帮你解决Android自定义View实现搜索框(SearchView)功能所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1143614.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存