AutoCompleteTextView下拉内容动态更新

1.绪论

一般来说,使用AutoCompleteTextView这个控件是给用户输入时提供选择提示的,而这个提示的列表是预先设置进去的,后面不在改变。但在现实场景中,这并不能满足需求,大部分需求是这个提示列表需根据输入的关键字通过网络请求查询,然后将查询的结果展示出来,供用户选择。也就是说提示列表是动态变化的。这样的需求使用AutoCompleteTextView控件怎么实现呢?

2.AutoCompleteTextView源码分析

要实现上面的需求就必须要了解AutoCompleteTextView是怎么工作的,它的实现逻辑如下:

1.AutoCompleteTextView 继承 EditText
2.需要ListAdapter并且要继承Filter类

public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
        if (mObserver == null) {
            mObserver = new PopupDataSetObserver(this);
        } else if (mAdapter != null) {
            mAdapter.unregisterDataSetObserver(mObserver);
        }
        mAdapter = adapter;
        if (mAdapter != null) {
            //noinspection unchecked
            mFilter = ((Filterable) mAdapter).getFilter();
            adapter.registerDataSetObserver(mObserver);
        } else {
            mFilter = null;
        }
        mPopup.setAdapter(mAdapter);
    }

3.添加监听addTextChangedListener

addTextChangedListener(new MyWatcher());
//下面MyWatcher类的实现
private class MyWatcher implements TextWatcher {
        public void afterTextChanged(Editable s) {
            doAfterTextChanged();
        }
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            doBeforeTextChanged();
        }
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }
    }
    //此方法是记录下拉框的显示状态
    void doBeforeTextChanged() {
        if (mBlockCompletion) return;
        // when text is changed, inserted or deleted, we attempt to show the drop down
        mOpenBefore = isPopupShowing();
    }
    //此方法是根据输入的内容找到匹配项,通过下拉框展示出来
    void doAfterTextChanged() {
        if (mBlockCompletion) return;
        if (mOpenBefore && !isPopupShowing()) {
            return;
        }
        // the drop down is shown only when a minimum number of characters was typed in the text view
        if (enoughToFilter()) {
            if (mFilter != null) {
                mPopupCanBeUpdated = true;
                performFiltering(getText(), mLastKeyCode);
            }
        } else {
            // drop down is automatically dismissed when enough characters are deleted from the text view
            if (!mPopup.isDropDownAlwaysVisible()) {
                dismissDropDown();
            }
            if (mFilter != null) {
                mFilter.filter(null);
            }
        }
    }

4.通过Filter类来找到匹配项,并通过popwindow展示出来

3.实现思路

从源码来看,当EditText内容改变时,就会立马从提示列表中找出匹配项,并展示出来;根本来不及去网络请求,然后将请求结果更新提示列表,再进行筛选。
或许此时,会有这样的一个想法,将网络请求更新提示列表这个动作放在Filter之前
执行不就好了。这个想法不可行,因为AutoCompleteTextView执行流程是不可控的。
既然流程不可控,那是否可以重新执行一遍此流程呢?当我们请求网络后,将结果更新提示列表,然后让系统重新执行doAfterTextChanged这个方法不就可以了么?很激动!!!

4.解决方案

想法很关键,代码实现不难,所以上面重点将源码分析和思路说了一下,当你认真看懂了源码,看懂系统是怎么实现此功能的,你才能在系统的基础上新增功能来实现自己的需求。下面是实现步骤:

1.监听EditText的addTextChangedListener
2.根据输入的内容请求网络,获取提示列表
3.更新列表,调用AutoCompleteTextView类的doAfterTextChanged方法,筛选并弹框

关键代码如下:

//添加兼听
       autoTVCheck.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }
            @Override
            public void afterTextChanged(Editable editable) {
                long time = DateUtil.getCurrentTimeInMills();
                //输入的字符间隔时间 小于700毫秒 移除以前的handler 延时600毫秒执行
                if (autoTVCheck.getTag() != null && time - (Long) autoTVCheck.getTag() < 700) {
                    searchHandler.removeMessages(1);
                }
                searchHandler.sendEmptyMessageDelayed(1, 600);
                autoTVCheck.setTag(time);
            }
        });
        //延时搜索的handler
        private Handler searchHandler = new Handler(Looper.getMainLooper()) {
        public void handleMessage(Message msg) {
            dealSearchHint();
        }
    };
    /**
     * 根据用户输入的字符去调用接口查询符合的订单或桌台名
     */
    private void dealSearchHint(){
        String searchContent = autoTVCheck.getText().toString();
        if(searchContent.isEmpty()){
            return;
        }
        //接口-模糊查询符合的订单或桌台名
        mProcess.getSearchDropList(searchContent,new ResultCallback<GetOrderFromCenterRespone>(){
                @Override
                public void onSuccess(GetOrderFromCenterRespone data) {
                    if(data != null && !ListUtil.isEmpty(data.orderList2)){
                    	//更新提示列表
                        searchAdapter.setOrderListModels(data.orderList2);
                        refreshDropList();
                    }
                }
        });
    }
    //AutoCompleteTextView的doBeforeTextChanged方法
    Method doBeforeTextChanged;
    //AutoCompleteTextView的doAfterTextChanged方法
    Method doAfterTextChanged;

    /**
     * 下拉提示框数据获取到后刷新此框
     * 通过反射调用AutoCompleteTextView的doBeforeTextChanged和doAfterTextChanged方法来实现刷新下拉提示框
     */
    private void refreshDropList(){
        try{
            if(doAfterTextChanged == null){
                Class autoCompleteTextView = Class.forName("android.widget.AutoCompleteTextView");
                doBeforeTextChanged = autoCompleteTextView.getDeclaredMethod("doBeforeTextChanged");
                doBeforeTextChanged.setAccessible(true);
                doAfterTextChanged = autoCompleteTextView.getDeclaredMethod("doAfterTextChanged");
                doAfterTextChanged.setAccessible(true);
            }
            autoTVCheck.showDropDown();
            doBeforeTextChanged.invoke(autoTVCheck);
            doAfterTextChanged.invoke(autoTVCheck);
        }catch (Exception e){}
    }

到此呢,AutoCompleteTextView下拉内容动态更新功能就写完了,从上一篇到现在编写这一篇,中间隔了将近一年,也是换工作以来,写的第一篇。可以看出来 换个环境真的很影响生活,影响工作,因为你要去适应和熟悉新的工作环境,要熟悉新工作的代码和以往的业务。当然,这些都是值得的!!

还是那句老话,如果屏前的你有一定的收获的话,那么请您给笔者点个赞或者留个言吧~

屏前的你,加油!!!

你可能感兴趣的:(android)