1. 程式人生 > >EditText+ListView實時重新整理ListView

EditText+ListView實時重新整理ListView

寫給自己和各位讀者:

       首先說本人目前還是個菜鳥,每當遇到問題時,第一時間想到的是在網上查詢大神的解決方案,其實在借鑑他人的方法時,也是在不斷地提升自己,不管是程式碼的閱讀能力,還是接收新知識,還是僅僅為以後的專案開發留下一個印象,都起到了很好的幫助。總之,多上網查詢資料,多看一些好的解決方法,當你在實際開發中遇到這些問題時你就可以信手拈來。下面是我最近碰到的,通過在文字框中輸入內容,如何實時重新整理ListView的問題。

下面是效果圖:

搜尋前:

搜尋後:

根據使用的ListView介面卡的不同,有兩種不同的實現方法。

第一種,如果你的ListView使用的是系統的ArrayAdapter,那麼這個事情就變得很簡單,只需要幾行程式碼就可以解決:

       //給編輯框新增監聽事件
        etSearch.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                //文字改變時,給adapter設定過濾器就行了,只需要一行程式碼
                listViewAdapter.getFilter().filter(charSequence);

            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });

第二種:由於大多數Adapter都是自定義的,所以不能直接呼叫上面這個方法,但是我們可以模仿ArrayAdapter來實現這個功能。那就是實現Filterable介面,通過實現Filterrable介面,我們就可以像上面那樣呼叫getFilter()和filter()方法了。

首先要有一個實體類,用來模擬資料。

public class AppInfoBitmap {
    private String mName;
    private Bitmap mBitmap;

    //無參構造
    public AppInfoBitmap() {
    }

    //帶參構造
    public AppInfoBitmap(String mName, Bitmap mBitmap) {
        this.mName = mName;
        this.mBitmap = mBitmap;
    }

    public String getmName() {
        return mName;
    }

    public void setmName(String mName) {
        this.mName = mName;
    }

    public Bitmap getmBitmap() {
        return mBitmap;
    }

    public void setmBitmap(Bitmap mBitmap) {
        this.mBitmap = mBitmap;
    }
}

接下來自定義一個Adapter(ListViewAdapter)繼承自BaseAdapter,實現了Filterable介面。

實現Filterable,必須要實現兩個方法。

分別是:

protected FilterResults performFiltering(CharSequence charSequence);
protected void publishResults(CharSequence charSequence, FilterResults filterResults);

其次我們向外提供了一個getFilter()方法,用來生成Filter物件,進而呼叫Filter類中的filter()方法,最終實現過濾功能。

public Filter getFilter() {
        if (mFilter==null){
            mFilter=new ArrayFilter();
        }
        return mFilter;
    }

下面就是比較完整的程式碼了(此處沒有包含Adapter一些常用處理,用最簡潔的程式碼來說明問題):

public class AppListViewAdapter extends BaseAdapter implements Filterable{

    private Context mContext;

    //Adapter資料來源
    private List<AppInfoBitmap> mAppInfoList;

    //過濾相關
    /*
    * 過濾器上的鎖可以同步複製原始資料
    * */
    private final Object mLock=new Object();

    //物件陣列的備份,當呼叫ArrayFilter的時候初始化和使用。此時,物件陣列只包含已經過濾的資料。
    private ArrayList<AppInfoBitmap> mOriginalValues;
    private ArrayFilter mFilter;




    @Override
    public Filter getFilter() {
        if (mFilter==null){
            mFilter=new ArrayFilter();
        }
        return mFilter;
    }



    //寫一個ArrayFilter類繼承Filter
    public class ArrayFilter extends Filter{

        //執行過濾的方法
        @Override
        protected FilterResults performFiltering(CharSequence charSequence) {
            /*
            * 一個帶有首字母約束的陣列過濾器,每一項
            * 不是以該首字母開頭的都會被移除該list
            * */
            //過濾的結果
            FilterResults results=new FilterResults();

            //原始資料為空時,上鎖,同步複製原始資料
            if (mOriginalValues==null){
                synchronized (mLock){
                    mOriginalValues=new ArrayList<>(mAppInfoList);
                }
            }

            //當首字母為空時
            if (charSequence==null||charSequence.length()==0){
                ArrayList<AppInfoBitmap> list;
                synchronized (mLock){
                    //同步複製一個原始備份資料
                    list=new ArrayList<>(mOriginalValues);
                }

                //此時返回的results就是原始的資料,不必進行過濾
                results.values=list;
                results.count=list.size();
            }else {
                //轉化為小寫
                String prefixString=charSequence.toString().toLowerCase() ;

                ArrayList<AppInfoBitmap> values;
                //同步複製一個原始備份資料
                synchronized (mLock){
                    values=new ArrayList<>(mOriginalValues);
                }

                final int count=values.size();
                final ArrayList<AppInfoBitmap> newValues =new ArrayList<>();

                for (int i = 0; i < count; i++) {
                    //從ArrayList<AppInfoBitmap>中拿到AppInfoBitmap物件
                    final AppInfoBitmap value=values.get(i);

                    /*
                    * 這個地方是,要進行搜尋的關鍵字,將app的name屬性當作關鍵字來進行篩選
                    * */
                    final String valueText=value.getmName().toString().toLowerCase();

                    /*
                    * 首字元匹配:valueText.startsWith(prefixString)
                    * 輸入的關鍵字包含在了某個item中:valueText.indexOf(prefixString.toString())!=-1
                    * */
                    if (valueText.startsWith(prefixString)|| valueText.indexOf(prefixString.toString())!=-1){
                        newValues.add(value);
                    }
                    else {
                        //處理首字元是空格,去掉text中的空格,然後賦值給字串words
                        final  String[] words=valueText.split(" ");
                        //獲取除去空格的字串的長度
                        final int wordCount=words.length;

                        //Start at index 0,in case valueText starts with space(s)
                        for (int k = 0; k < wordCount; k++) {
                            if (words[k].startsWith(prefixString)){
                                //一旦找到匹配的就break,跳出for迴圈
                                newValues.add(value);
                                break;
                            }
                        }

                    }
                }

                //此時的results就是過濾後的List<AppInfoBitmap>
                results.values=newValues;
                results.count=newValues.size();
            }

            //將結果返回
            return results;
        }

        //篩選結果
        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
            //此時,Adapter資料來源就是過濾後的esults
            mAppInfoList=(List<AppInfoBitmap>) filterResults.values;

            /*
             * 資料容器變化------->notifyDataSetInValidated
             * 容器中資料發生變化------->notifyDataSetChanged
             * */
            if (filterResults.count>0){
                //這個相當於從mAppInfoList中刪除了一些資料,只是資料的變化,所以使用notifyDataSetChanged()
                notifyDataSetChanged();
            }else {

                //當results.count<=0,此時資料來源就是重新new出來的,說明原始資料來源已經失效了
                notifyDataSetInvalidated();
            }

        }
    }
}

下面是我的完整的Aadpter程式碼,希望可以幫助到陷入沉思的你。

public class AppListViewAdapter extends BaseAdapter implements Filterable{

    private Context mContext;

    //Adapter資料來源
    private List<AppInfoBitmap> mAppInfoList;

    //過濾相關
    /*
    * 過濾器上的鎖可以同步複製原始資料
    * */
    private final Object mLock=new Object();

    //物件陣列的備份,當呼叫ArrayFilter的時候初始化和使用。此時,物件陣列只包含已經過濾的資料。
    private ArrayList<AppInfoBitmap> mOriginalValues;
    private ArrayFilter mFilter;

    //帶參構造
    public AppListViewAdapter(Context context, List<AppInfoBitmap> appInfoList) {
        this.mContext = context;
        this.mAppInfoList = appInfoList;
    }

    @Override
    public int getCount() {
        return mAppInfoList.size();
    }

    @Override
    public AppInfoBitmap getItem(int i) {
        return mAppInfoList.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    //通過ViewHolder優化listView
    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder viewHolder=new ViewHolder();
        if (view==null){
            view=View.inflate(mContext, R.layout.activity_app_list_item,null);

            //獲取佈局控制元件
            viewHolder.imageView=view.findViewById(R.id.app_image);
            viewHolder.textView=view.findViewById(R.id.app_name);

            //設定佈局控制元件
            viewHolder.imageView.setImageBitmap(mAppInfoList.get(i).getmBitmap());
            viewHolder.textView.setText(mAppInfoList.get(i).getmName());

            //設定Tag
            view.setTag(viewHolder);
        }else {
            //獲取Tag
            viewHolder=(ViewHolder) view.getTag();

            //設定佈局控制元件
            viewHolder.imageView.setImageBitmap(mAppInfoList.get(i).getmBitmap());
            viewHolder.textView.setText(mAppInfoList.get(i).getmName());
        }

        //將view檢視返回
        return view;
    }

    public class ViewHolder{
        ImageView imageView;
        TextView textView;
    }

    @Override
    public Filter getFilter() {
        if (mFilter==null){
            mFilter=new ArrayFilter();
        }
        return mFilter;
    }

    //寫一個ArrayFilter類繼承Filter
    public class ArrayFilter extends Filter{

        //執行過濾的方法
        @Override
        protected FilterResults performFiltering(CharSequence charSequence) {
            /*
            * 一個帶有首字母約束的陣列過濾器,每一項
            * 不是以該首字母開頭的都會被移除該list
            * */
            //過濾的結果
            FilterResults results=new FilterResults();

            //原始資料為空時,上鎖,同步複製原始資料
            if (mOriginalValues==null){
                synchronized (mLock){
                    mOriginalValues=new ArrayList<>(mAppInfoList);
                }
            }

            //當首字母為空時
            if (charSequence==null||charSequence.length()==0){
                ArrayList<AppInfoBitmap> list;
                synchronized (mLock){
                    //同步複製一個原始備份資料
                    list=new ArrayList<>(mOriginalValues);
                }

                //此時返回的results就是原始的資料,不必進行過濾
                results.values=list;
                results.count=list.size();
            }else {
                //轉化為小寫
                String prefixString=charSequence.toString().toLowerCase() ;

                ArrayList<AppInfoBitmap> values;
                //同步複製一個原始備份資料
                synchronized (mLock){
                    values=new ArrayList<>(mOriginalValues);
                }

                final int count=values.size();
                final ArrayList<AppInfoBitmap> newValues =new ArrayList<>();

                for (int i = 0; i < count; i++) {
                    //從ArrayList<AppInfoBitmap>中拿到AppInfoBitmap物件
                    final AppInfoBitmap value=values.get(i);

                    /*
                    * 這個地方是,要進行搜尋的關鍵字,將app的name屬性當作關鍵字來進行篩選
                    * */
                    final String valueText=value.getmName().toString().toLowerCase();

                    /*
                    * 首字元匹配:valueText.startsWith(prefixString)
                    * 輸入的關鍵字包含在了某個item中,位置不做考慮
                    * */
                    if (valueText.startsWith(prefixString)|| valueText.indexOf(prefixString.toString())!=-1){
                        newValues.add(value);
                    }
                    else {
                        //處理首字元是空格,去掉text中的空格,然後賦值給字串words
                        final  String[] words=valueText.split(" ");
                        //獲取除去空格的字串的長度
                        final int wordCount=words.length;

                        //Start at index 0,in case valueText starts with space(s)
                        for (int k = 0; k < wordCount; k++) {
                            if (words[k].startsWith(prefixString)){
                                //一旦找到匹配的就break,跳出for迴圈
                                newValues.add(value);
                                break;
                            }
                        }

                    }
                }

                //此時的results就是過濾後的List<AppInfoBitmap>
                results.values=newValues;
                results.count=newValues.size();
            }

            //將結果返回
            return results;
        }

        //篩選結果
        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
            //此時,Adapter資料來源就是過濾後的esults
            mAppInfoList=(List<AppInfoBitmap>) filterResults.values;

            /*
             * 資料容器變化------->notifyDataSetInValidated
             * 容器中資料發生變化------->notifyDataSetChanged
             * */
            if (filterResults.count>0){
                //這個相當於從mAppInfoList中刪除了一些資料,只是資料的變化,所以使用notifyDataSetChanged()
                notifyDataSetChanged();
            }else {

                //當results.count<=0,此時資料來源就是重新new出來的,說明原始資料來源已經失效了
                notifyDataSetInvalidated();
            }

        }
    }
}