SearchView配合RecyclerView實(shí)現(xiàn)分頁(yè)搜索過(guò)濾關(guān)鍵字

在日常開發(fā)中栋荸,我們會(huì)遇到一種需求豪娜,就是通過(guò)輸入關(guān)鍵字快速的查詢當(dāng)前列表中的數(shù)據(jù)并進(jìn)行過(guò)濾顯示吁津。(感覺好難用文字描述這個(gè)功能啊·····)棚蓄,在網(wǎng)上找了一些資料也沒(méi)有類似的文章,只好自己變嘗試變查資料碍脏,用了幾個(gè)小時(shí)的時(shí)間終于搞出來(lái)了梭依。
OK,看圖:

1.gif

組件準(zhǔn)備

1.SearchView:SearchView是Android原生的搜索框控件,它提供了一個(gè)用戶界面典尾,用于用戶搜索查詢役拴。
SearchView默認(rèn)是展示一個(gè)search的icon,點(diǎn)擊icon展開搜索框钾埂,如果你想讓搜索框默認(rèn)就展開河闰,可以通過(guò)setIconifiedByDefault(false);實(shí)現(xiàn)。

2.XRecyclerView: github地址
這個(gè)組件主要是對(duì)RecyclerView的封裝褥紫,主要完成了下拉刷新姜性、上拉加載更多、RecyclerView頭部故源。
關(guān)于這個(gè)組件的詳解污抬,請(qǐng)參考這篇文章:文章地址

開始布局

現(xiàn)在先進(jìn)行布局:


<android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/window_background"
    android:fitsSystemWindows="true"
    android:focusable="true"
    android:focusableInTouchMode="true">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/app_main_color"
            android:minHeight="?attr/actionBarSize"
            app:layout_scrollFlags="scroll"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:theme="@style/ThemeOverlay.AppCompat.ActionBar"
            app:title="@string/location_detail"
            app:titleTextColor="@color/white" />

        <SearchView
            android:id="@+id/searchView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/spacing_small"
            android:iconifiedByDefault="false"
            android:queryHint="@string/search_hint"
            android:textColor="@color/white"
           />

    </android.support.design.widget.AppBarLayout>


    <com.jcodecraeer.xrecyclerview.XRecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:animateLayoutChanges="false"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <TextView
        android:id="@+id/tv_emptyView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:background="#dcdcdc"
        android:text="無(wú)數(shù)據(jù)" />

</android.support.design.widget.CoordinatorLayout>

這個(gè)布局是我項(xiàng)目中的布局方式,所以你不必完全按照這個(gè)寫法來(lái)绳军,主要關(guān)注SearchView和XRecyclerView的寫法就可以了印机,OK,我們先看SearchView:

android:iconifiedByDefault="false"

由于SearchView默認(rèn)是一個(gè)搜索放大鏡的圖標(biāo)门驾,在點(diǎn)擊后才會(huì)展開輸入框輸入搜索關(guān)鍵字射赛,但是在我的項(xiàng)目中是需要搜索框默認(rèn)就展開的,所以需要設(shè)置這個(gè)屬性奶是,當(dāng)屬性設(shè)置為true時(shí)楣责,搜索框不展開,當(dāng)屬性設(shè)置為false時(shí)聂沙,搜索框展開秆麸。

 android:queryHint="@string/search_hint"

這個(gè)是搜索框的提示內(nèi)容

android:textColor="@color/white"

這個(gè)是搜索框的輸入字體顏色

SearchVieW基本上就是這些設(shè)置了,當(dāng)然如果你打算只讓搜索框內(nèi)輸入數(shù)字及汉,可以利用SearcheView的android:inputType屬性來(lái)設(shè)置沮趣。

再來(lái)看看XRecyclerView:
這個(gè)組件基本上不需要什么特殊的設(shè)置,只有一個(gè)屬性

app:layout_behavior="@string/appbar_scrolling_view_behavior"

這個(gè)是配合AppBarLayout和Toolbar使用的滾動(dòng)監(jiān)聽坷随,當(dāng)向上滾動(dòng)時(shí)可以動(dòng)態(tài)的隱藏Toolbar,不過(guò)這里和本文沒(méi)有什么關(guān)系房铭,只是順帶一提驻龟。

邏輯分析

在開發(fā)之前,我習(xí)慣先進(jìn)行一些思考缸匪,把要實(shí)現(xiàn)的功能在腦子里實(shí)現(xiàn)一遍翁狐,主要考慮的不是代碼細(xì)節(jié)而是邏輯判斷多一些,對(duì)于這個(gè)功能凌蔬,主要考慮幾點(diǎn):
1.SearchView是否有一個(gè)監(jiān)聽露懒,可以監(jiān)聽我的輸入內(nèi)容。
2.下拉刷新的時(shí)候砂心,應(yīng)當(dāng)把SearchView中的輸入內(nèi)容全部清空隐锭,并且讓SearchView不再獲取焦點(diǎn),從而關(guān)閉輸入法计贰,增加用戶體驗(yàn)。
3.應(yīng)當(dāng)實(shí)時(shí)判斷SearchView中的數(shù)據(jù)蒂窒,如果SearchView中含有數(shù)據(jù)的話躁倒,則關(guān)閉XRecyclerView的加載更多方法, 不允許用戶在這個(gè)界面下加載更多洒琢,直到SearchView中輸入的數(shù)據(jù)為空秧秉,則再次開啟下拉加載更多的功能。
4.要在Acitivity中寫一個(gè)過(guò)濾方法衰抑,目的是為了得到符合條件的數(shù)據(jù)源(ArrayList)
5.Adapter中要一個(gè)設(shè)置過(guò)濾的方法象迎,目的是為了將過(guò)濾后的數(shù)據(jù)傳入Adapter并刷新數(shù)據(jù)。
6.Adapter中還要設(shè)置一個(gè)關(guān)閉過(guò)濾的方法呛踊, 目的是在上拉加載更多的時(shí)候?qū)?dāng)前數(shù)據(jù)源傳遞給Adapter并刷新數(shù)據(jù)砾淌。(這一步開始并沒(méi)有考慮到,直到發(fā)現(xiàn)每次搜索完畢后谭网,再上拉加載時(shí)明明能獲取數(shù)據(jù)可是再界面中卻無(wú)法顯示數(shù)據(jù)時(shí)才想到的汪厨。)

代碼實(shí)現(xiàn)

在代碼中已經(jīng)將之前的邏輯分析都注釋出來(lái)了。

Activity:


/**
 * 創(chuàng)建人:賈真
 * 創(chuàng)建時(shí)間:2016/9/9 10:01
 * 修改備注:
 */
public class CarLocationListActivity extends OBaseActivity {
    private static final String TAG = CarLocationListActivity.class.getSimpleName();
    @BindView(R.id.toolbar)
    Toolbar toolbar;
    @BindView(R.id.recyclerview)
    XRecyclerView mRecyclerView;
    @BindView(R.id.searchView)
    SearchView searchView;
    @BindView(R.id.tv_emptyView)
    TextView tvEmptyView;
    private CarLocationListAdapter mAdapter;
    //數(shù)據(jù)存儲(chǔ)列表
    private ArrayList<CarLocationListModel> listData;
    //當(dāng)前頁(yè)數(shù)
    int pageNo = 1;
    //每頁(yè)顯示數(shù)
    int pageSize = 15;

    @Override
    protected void init() {
        setContentView(R.layout.activity_locationcar_list);
        ButterKnife.bind(this);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(layoutManager);
        mRecyclerView.setRefreshProgressStyle(ProgressStyle.BallSpinFadeLoader);
        mRecyclerView.setLoadingMoreProgressStyle(ProgressStyle.BallRotate);
        mRecyclerView.setArrowImageView(R.drawable.iconfont_downgrey);
        mRecyclerView.setLoadingMoreEnabled(true);
        mRecyclerView.setEmptyView(tvEmptyView);


    }

    private void GetPageListData(int pageNo, int pageSize) {

        appAction.GetCarLocationList(PublicDefault.USERID, pageNo, pageSize, new onNetWorkListener<ArrayList<CarLocationListModel>>() {

            @Override
            public void onSuccess(ArrayList<CarLocationListModel> model) {

                if (mAdapter == null) {
                    listData = model;
                    mAdapter = new CarLocationListAdapter(listData);
                    mRecyclerView.setAdapter(mAdapter);
                    mRecyclerView.refreshComplete();
                } else {
                    //將獲取的元素全部加入到列表的尾部
                    listData.addAll(model);
                    mAdapter.closeFilter(listData);
                    mRecyclerView.loadMoreComplete();
                }
            }

            @Override
            public void onFailure(int errorEvent, String message) {
                ToastUtil.ErrorImageToast(CarLocationListActivity.this, getResources().getString(R.string.get_location_list_fail));
                if (mAdapter == null) {

                    mRecyclerView.refreshComplete();
                } else {

                    mRecyclerView.loadMoreComplete();
                }


            }
        });
    }

    @Override
    protected void setListeners() {
        /**
         * 列表下拉刷新和上拉加載的監(jiān)聽方法
         * 下拉刷新時(shí)要將頁(yè)數(shù)重新設(shè)置為1 并且將數(shù)據(jù)清空 還要將適配器清理掉 并且要將搜索文字清理掉
         *
         */
        mRecyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {
            @Override
            public void onRefresh() {
                //邏輯2:下拉刷新時(shí)愉择,將SearchView中的數(shù)據(jù)清空劫乱,并讓SearchView失去焦點(diǎn)。
                searchView.setQuery("", false);
                searchView.clearFocus();

                pageNo = 1;
                if (listData != null)
                    listData.clear();
                mAdapter = null;
                GetPageListData(pageNo, pageSize);
            }

            @Override
            public void onLoadMore() {
                pageNo++;
                GetPageListData(pageNo, pageSize);

            }
        });
        mRecyclerView.setRefreshing(true);

        //邏輯1:SearchView的監(jiān)聽輸入內(nèi)容事件:監(jiān)聽查詢內(nèi)容锥涕,onQueryTextSubmit是提交監(jiān)聽衷戈,不符合我的需求 onQueryTextChange是實(shí)時(shí)監(jiān)聽,符合我們的需求
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            // 當(dāng)點(diǎn)擊搜索按鈕時(shí)觸發(fā)該方法
            @Override
            public boolean onQueryTextSubmit(String s) {
                return false;
            }

            // 當(dāng)搜索內(nèi)容改變時(shí)觸發(fā)該方法
            @Override
            public boolean onQueryTextChange(String searchText) {
                //邏輯3:當(dāng)搜索框中存在搜索數(shù)據(jù)則關(guān)閉加載更多
                if (!"".equals(searchView.getQuery().toString().trim())) {
                    L.d(TAG, "關(guān)閉加載更多");
                    mRecyclerView.setLoadingMoreEnabled(false);
                } else {
                    L.d(TAG, "開啟加載更多");
                    mRecyclerView.setLoadingMoreEnabled(true);
                }
                final ArrayList<CarLocationListModel> filteredModelList = filter(listData, searchText);
                mAdapter.setFilter(filteredModelList);
                return true;
            }
        });

        searchView.setOnCloseListener(new SearchView.OnCloseListener() {
            @Override
            public boolean onClose() {
                L.d(TAG, "onClose");
                searchView.setQuery("", false);
                searchView.clearFocus();
                return true;
            }
        });
    }

    @Override
    protected void stop() {
        mRecyclerView = null;
        toolbar = null;
        mAdapter = null;
        if (listData != null) {
            listData.clear();
            listData = null;
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     *
     * 邏輯4:過(guò)濾方法层坠,目的是過(guò)濾符合當(dāng)前數(shù)據(jù)中符合條件的數(shù)據(jù)源
     * @param models
     * @param query
     * @return
     */
    private ArrayList<CarLocationListModel> filter(ArrayList<CarLocationListModel> models, String query) {

        query = query.toLowerCase();

        final ArrayList<CarLocationListModel> filteredModelList = new ArrayList<>();

        for (CarLocationListModel model : models) {

            final String text = model.getChePaiHao().toLowerCase();

            if (text.contains(query)) {

                filteredModelList.add(model);

            }

        }

        return filteredModelList;

    }

}


Adapter:


public class CarLocationListAdapter extends RecyclerView.Adapter<LocationListViewHolder> {
    public ArrayList<CarLocationListModel> datas = null;

    public CarLocationListAdapter(ArrayList<CarLocationListModel> datas) {
        this.datas = datas;
    }

    //創(chuàng)建新View殖妇,被LayoutManager所調(diào)用
    @Override
    public LocationListViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.adapter_locationcar_list, viewGroup, false);
        return new LocationListViewHolder(view);
    }

    @Override
    public void onBindViewHolder(LocationListViewHolder viewHolder, int position) {
        viewHolder.tvCarNo.setText(String.valueOf(datas.get(position).getChePaiHao()));
        viewHolder.tvSpeed.setText(datas.get(position).getCheSu()+"km/h");
        viewHolder.tvCarAddress.setText("位置:"+ TextUtil.checkText(datas.get(position).getDangQianWeiZhi()));
        viewHolder.tvJrlc.setText("今日里程:"+datas.get(position).getJinRiLiCheng()+"Km");
        viewHolder.tvJryh.setText("今日油耗:"+datas.get(position).getJinRiYouHao()+"L");
    }

    //獲取數(shù)據(jù)的數(shù)量
    @Override
    public int getItemCount() {
        return datas.size();
    }

    /**
     * 邏輯5:在Adapter中設(shè)置一個(gè)過(guò)濾方法,目的是為了將過(guò)濾后的數(shù)據(jù)傳入Adapter中并刷新數(shù)據(jù)
     * @param locationListModels
     */
    public void setFilter(ArrayList<CarLocationListModel> locationListModels ) {

        datas = new ArrayList<>();

        datas .addAll( locationListModels );

        notifyDataSetChanged();

    }

    /**
     *邏輯6:
     * 設(shè)置一個(gè)關(guān)閉過(guò)濾的方法窿春, 目的是在上拉加載更多的時(shí)候?qū)⒄鎸?shí)數(shù)據(jù)源傳遞給Adapter并刷新數(shù)據(jù)
     * @param allList
     */
    public void closeFilter(ArrayList<CarLocationListModel> allList){

        datas=allList;
        notifyDataSetChanged();
    }

}

這里就只貼出來(lái)Activity和Adapter和主Activity的布局文件拉一,其他的ViewHolder和Adapter的布局文件采盒,大家在實(shí)現(xiàn)的時(shí)候可以寫簡(jiǎn)單一點(diǎn)的,我這里就不貼出來(lái)了蔚润。
只要是跟著這個(gè)教程走的話磅氨,基本上是不會(huì)有什么太大的問(wèn)題的,剩下的就是按部就班的進(jìn)行開發(fā)就可以了嫡纠。

總結(jié)

其實(shí)這個(gè)效果實(shí)現(xiàn)起來(lái)并不難烦租,關(guān)鍵是要分析清楚各種邏輯關(guān)系,如果記性好可以用腦子理清這些邏輯關(guān)系除盏,如果記性沒(méi)有那么好叉橱,就自己寫出這些邏輯關(guān)系吧,畢竟好記性不如爛筆頭嘛者蠕,好了 拋磚至此窃祝,多謝您看完。

引用

1.Android 搜索框:SearchView 的屬性和用法詳解
2.Android Filter RecyclerView Using SearchView In ToolBar

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踱侣,一起剝皮案震驚了整個(gè)濱河市粪小,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抡句,老刑警劉巖探膊,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異待榔,居然都是意外死亡逞壁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門锐锣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)腌闯,“玉大人,你說(shuō)我怎么就攤上這事刺下“筻冢” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵橘茉,是天一觀的道長(zhǎng)工腋。 經(jīng)常有香客問(wèn)我,道長(zhǎng)畅卓,這世上最難降的妖魔是什么擅腰? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮翁潘,結(jié)果婚禮上趁冈,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好渗勘,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布沐绒。 她就那樣靜靜地躺著,像睡著了一般旺坠。 火紅的嫁衣襯著肌膚如雪乔遮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天取刃,我揣著相機(jī)與錄音蹋肮,去河邊找鬼。 笑死璧疗,一個(gè)胖子當(dāng)著我的面吹牛坯辩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播崩侠,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼漆魔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了却音?” 一聲冷哼從身側(cè)響起有送,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎僧家,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體裸删,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡八拱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涯塔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肌稻。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖匕荸,靈堂內(nèi)的尸體忽然破棺而出爹谭,到底是詐尸還是另有隱情,我是刑警寧澤榛搔,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布诺凡,位于F島的核電站,受9級(jí)特大地震影響践惑,放射性物質(zhì)發(fā)生泄漏腹泌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一尔觉、第九天 我趴在偏房一處隱蔽的房頂上張望凉袱。 院中可真熱鬧,春花似錦、人聲如沸专甩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涤躲。三九已至棺耍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間篓叶,已是汗流浹背烈掠。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缸托,地道東北人左敌。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像俐镐,于是被迫代替她去往敵國(guó)和親矫限。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,285評(píng)論 25 707
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 46,791評(píng)論 22 665
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程佩抹,因...
    小菜c閱讀 6,444評(píng)論 0 17
  • SearchView是安卓自帶的搜索控件叼风,可以幫助我們省下很多功夫。SearchView提供的api很多棍苹,但是麻煩...
    T_濤_T閱讀 5,901評(píng)論 0 3
  • 一无宿、安裝 使用MyBatis,首先需要下載其jar包,然后將jar包導(dǎo)入lib中即可枢里,下載地址:github.co...
    涉務(wù)閱讀 268評(píng)論 0 0