一般来说,使用AutoCompleteTextView这个控件是给用户输入时提供选择提示的,而这个提示的列表是预先设置进去的,后面不在改变。但在现实场景中,这并不能满足需求,大部分需求是这个提示列表需根据输入的关键字通过网络请求查询,然后将查询的结果展示出来,供用户选择。也就是说提示列表是动态变化的。这样的需求使用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展示出来
从源码来看,当EditText内容改变时,就会立马从提示列表中找出匹配项,并展示出来;根本来不及去网络请求,然后将请求结果更新提示列表,再进行筛选。
或许此时,会有这样的一个想法,将网络请求更新提示列表这个动作放在Filter之前
执行不就好了。这个想法不可行,因为AutoCompleteTextView执行流程是不可控的。
既然流程不可控,那是否可以重新执行一遍此流程呢?当我们请求网络后,将结果更新提示列表,然后让系统重新执行doAfterTextChanged这个方法不就可以了么?很激动!!!
想法很关键,代码实现不难,所以上面重点将源码分析和思路说了一下,当你认真看懂了源码,看懂系统是怎么实现此功能的,你才能在系统的基础上新增功能来实现自己的需求。下面是实现步骤:
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下拉内容动态更新功能就写完了,从上一篇到现在编写这一篇,中间隔了将近一年,也是换工作以来,写的第一篇。可以看出来 换个环境真的很影响生活,影响工作,因为你要去适应和熟悉新的工作环境,要熟悉新工作的代码和以往的业务。当然,这些都是值得的!!
还是那句老话,如果屏前的你有一定的收获的话,那么请您给笔者点个赞或者留个言吧~
屏前的你,加油!!!