国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自定義View實現搜索框(SearchView)功能

2019-10-22 18:22:35
字體:
來源:轉載
供稿:網友

概述

在Android開發中,當系統數據項比較多時,常常會在app添加搜索功能,方便用戶能快速獲得需要的數據。搜索欄對于我們并不陌生,在許多app都能見到它,比如豌豆莢

Android,View,搜索框,SearchView

在某些情況下,我們希望我們的自動補全信息可以不只是純文本,還可以像豌豆莢這樣,能顯示相應的圖片和其他數據信息,因此Android給我們提供的AutoCompleteTextView往往就不夠用,在大多情況下我們都需要自己去實現搜索框。

分析

根據上面這張圖,簡單分析一下自定義搜索框的結構與功能,有
1. 搜索界面大致由三部門組成,如圖:輸入框+(自動補全)提示框+結果列表。
2. 提示框的數據與輸入框輸入的文本是實時聯動的,而結果列表只有在每次進行搜索操作時才會更新數據

3. 輸入框的UI應是動態的,即UI隨著輸入的文本的改變而改變,如:在未輸入文本時,清除按鈕Android,View,搜索框,SearchView應該是隱藏的;只有當框中有文本時才會顯示。
4. 軟鍵盤也應該是動態的,如完成搜索時應自動隱藏。
5. 選擇提示框的選項會自動補全輸入框,且自動進行搜索
6. (external)有熱門搜索推薦/記錄搜索記錄的功能——熱門搜索推薦列表只在剛要進行搜索的時候彈出,即未輸入文本時,可供用戶選擇。

根據上面的分析,我們認為一個搜索框應該包含輸入框和提示框兩個部分。搜索框可以設置一個回調監聽接口,當需要進行搜索操作時,調用監聽者的search()方法,從而實現具體的搜索操作以及結果列表的數據聯動。

演示Demo

Android,View,搜索框,SearchView

注意:

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;    /**    * 彈出列表    */   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 i, int i2, int i3) {      }      @Override     public void onTextChanged(CharSequence charSequence, int i, int i2, 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);   }  } 

搜索框主要包含兩個結構:輸入欄+彈出框(自動補全或熱門搜素推薦)。

代碼不多,實現很簡單,主要是需要給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, View view, 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, android.R.layout.simple_list_item_1, 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性能更優化。

自動補全匹配也是采用相同的算法。算法都比較簡單,當然也可以弄得復雜點,比如根據“ ”(空格)去分割輸入文本,再逐個考慮單個搜索詞的匹配項,把匹配次數從多到少排列出結果集等等。這里不細說。

這里有一個問題是進入該搜索界面時需要加載所有的數據項到內存,當數據項很多時,是否會占用大量的內存?如果是應該如何避免?是采用只加載一部分數據的形式,還是直接使用搜索詞到數據庫中查詢更優?還請各位看官大神們給出寶貴的意見~

好了,自定義搜索框到這就打造完成啦,是不是感覺簡單過頭了。

各位看官如果有任何問題可評論或者發郵件跟我聯系yetwish@gmail.com

囧~忘記貼代碼了,代碼放在github上,各位看官直接download即可
鏈接:https://github.com/yetwish/CustomSearchView

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 灵寿县| 游戏| 阳城县| 三门县| 西峡县| 固阳县| 闻喜县| 揭西县| 新建县| 淮阳县| 明星| 台北市| 中西区| 关岭| 和顺县| 东兰县| 海安县| 永宁县| 鲁甸县| 乐至县| 鄂州市| 鹿泉市| 襄城县| 安龙县| 哈巴河县| 普宁市| 濮阳市| 双牌县| 内黄县| 竹山县| 顺平县| 霍林郭勒市| 玛沁县| 阜城县| 连江县| 沽源县| 曲麻莱县| 卓尼县| 军事| 凉山| 清新县|