我是靠谱客的博主 整齐发夹,最近开发中收集的这篇文章主要介绍AutoComleteTextView最佳实践-原理剖析篇AutoComleteTextView最佳实践-原理剖析篇,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

AutoComleteTextView最佳实践-原理剖析篇

banner

此系列文章记录了一次使用AutoCompleteTextView(以下简称ACTV)的踩坑过程,并复盘整个的解决流程。本文着重讲解ACTV触发候选列表展示的代码总流程,深入了解Android的控件传递事件的机制。

以下是此系列所有文章

  • 《AutoCompleteTextView最佳实践-总集篇》
  • 《AutoCompleteTextView最佳实践-最简例子篇》
  • 《AutoCompleteTextView最佳实践-原理剖析篇》
  • 《AutoCompleteTextView最佳实践-键盘事件篇》
  • 《AutoCompleteTextView最佳实践-其他功能篇》(未完成)

关于作者

景三,程序员,主要从事Android平台基础架构方面的工作,欢迎交流技术方面的问题,可以去我的Github提issue或者发邮件至relish.wang@gmail.com与我交流。

首先我们想到ACTV是在输入框的内容文字改变的时候回触发候选列表展示。由于ACTV继承自EditText,我们就想到了这个功能一定是配合TextWatcher实现的。接下来我们来寻找这个TextWatcher

ACTV有多个构造方法,所有的构造方法,最终都调用了参数最多的构造方法。果不其然,我们在这里找到它为自己设置了一个TextWatcher

ACTV的构造方法:

public AutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes, Theme popupTheme) {
super(context, attrs, defStyleAttr, defStyleRes);
// ...省略部分代码...
// 添加输入框内容文字变化的监听器
addTextChangedListener(new MyWatcher());
// ...省略部分代码...
}

MyWatcher:

// MyTextWatcher是ACTV的一个私有内部类
private class MyWatcher implements TextWatcher {
public void afterTextChanged(Editable s) {
doAfterTextChanged();// 调用了ACTV的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 doAfterTextChanged() {
// ...省略部分代码...
// 判断是否可以展示候选列表(前文提过ACTV有一个completionThreshold属性, 设置的数字的值代表,达到多少个字符就展示候选列表)
if (enoughToFilter()) {
if (mFilter != null) {
mPopupCanBeUpdated = true;
performFiltering(getText(), mLastKeyCode); // 进入这个方法中继续查看
}
} else {
// ...省略部分代码...
}
}

根据前面贴出的部分源码可知,代码流程走入了MyWatcher里的afterTextChanged,afterTextChanged又调用了ACTV的performFiltering方法。

ACTV#performFiltering:

protected void performFiltering(CharSequence text, int keyCode) {
// 这里的第二个参数(this)传入的其实是一个FilterListener。
// (ACTV实现了FilterListener的onFilterComplete方法)
mFilter.filter(text, this);
}

performFiltering里直接调用了mFilterfilter方法。我们来找一下mFilter这个对象是从哪里来的。

纵观整个ACTV的源码我们发现mFilter只有一处被赋值的地方,就在ACTV的setAdapter方法里:

ACTV#setAdapter:

public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
// ...省略部分代码...
if (mAdapter != null) {
mFilter = ((Filterable) mAdapter).getFilter();// 获取Adapter中的Filter对象
// ...省略部分代码...
} else {
mFilter = null;
}
// ...省略部分代码...
}

由上面贴出的源码节选可知,mFilter来自于我们外部为ACTV设置的适配器中。找到了mFilter的来源,那我们看一下前文提到调用了mFilterfilter方法。

Filter#filter:

public final void filter(CharSequence constraint, FilterListener listener) {
synchronized (mLock) {
// ...省略部分代码...
mThreadHandler = new RequestHandler(thread.getLooper());
Message message = mThreadHandler.obtainMessage(FILTER_TOKEN);
RequestArguments args = new RequestArguments();
args.constraint = constraint != null ? constraint.toString() : null;
args.listener = listener;
message.obj = args;
// 向mThreadHandler发送了what为FILTER_TOKEN的Message
mThreadHandler.sendMessageDelayed(message, delay);
// ...省略部分代码...
}
}

Filter#RequestHandler#handleMessage:

private class RequestHandler extends Handler {
public void handleMessage(Message msg) {
int what = msg.what;
switch (what) {
case FILTER_TOKEN:
RequestArguments args = (RequestArguments) msg.obj;
// performFiltering具体实现在Adapter中
args.results = performFiltering(args.constraint);
Message message = mResultHandler.obtainMessage(what);
message.obj = args;
message.sendToTarget();
// ...省略部分代码...
break;
// ...省略部分代码...
}
}
}

看到这里的args.result数据来源于performFiltering方法,这个方法在Filter中是一个抽象方法,具体实现就是在我们为ACTV设置的Adapter中。

Filter#performFiltering:

这个方法实现的代码前文没贴,不过没关系,笔者来说明。这个方法是用来根据ACTV输入框里的关键字来过滤出需要展示候选数据的List。performFiltering中就是实现具体的过滤规则(比如匹配一下用户的手机号,姓名等)。

上面我们看到performFiltering的数据传入Massage被发送到了mResultHandler里我们来看一下mResultHandlerhandleMessage

Filter#ResultsHandler:

private class ResultsHandler extends Handler {
@Override
public void handleMessage(Message msg) {
RequestArguments args = (RequestArguments) msg.obj;
// 将performFiltering返回的数据作为入参传入publishResults
publishResults(args.constraint, args.results);
if (args.listener != null) {
int count = args.results != null ? args.results.count : -1;
// 再将过滤出来的数据量传给Listener,通知设置监听器的一方过滤操作结束且告知过滤后的数量
args.listener.onFilterComplete(count);
}
}
}

Filter#publishResults

最后performFiltering返回的过滤结果传入publishResultspublishResults就负责将数据展示出来就行了(设置数据源,调用Adapter的notifiyDataSetChanged)。

ACTV#onFilterComplete:——(ACTV实现FilterListener#publishResults)

publishResults之后就会调用后FilterListener#onFilterComplete, 这个监听器在ACTV中有实现:

public void onFilterComplete(int count) {
updateDropDownForFilter(count);// 调用下方的方法
}
private void updateDropDownForFilter(int count) {
// ...省略部分代码...
// (展示数据数量大于0 或 候选窗口总是展示)且 输入文字足够触发过滤器
if ((count > 0 || dropDownAlwaysVisible) && enoughToFilter) {
if (hasFocus() && hasWindowFocus() && mPopupCanBeUpdated) {
showDropDown(); // 展示候选窗口
}
}
// ...省略部分代码...
}

ACTV#showDropDown

/**
* <p>Displays the drop down on screen.</p>
*/
public void showDropDown() {
// ...省略部分代码...
mPopup.show();
// ...省略部分代码...
}

mPopup#show:

/**
* Show the popup list. If the list is already showing, this method
* will recalculate the popup's size and position.
*/
@Override
public void show() {
int height = buildDropDown();// 1 在show的时候会动态计算高度
// ...省略部分代码...
if (mPopup.isShowing()) {
// ...省略部分代码...
} else {
final int heightSpec;
if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
heightSpec = height;
} else {
heightSpec = mDropDownHeight;
}
}
mPopup.setWidth(widthSpec);
mPopup.setHeight(heightSpec); // 2 并设置为mPopup的高度
}
}

通过上述的代码流程梳理,我们大概知道了ACTV的工作原理。我们再简单整理一下过程:

  • 1 ACTV设置TextWatcher

  • 2 TextWatcher的afterTextChanged中执行了mFilter.filter方法(mFilter来自我们为ACTV设置的Adapter)

  • 3 mFilter.filter方法先执行了performFiltering, performFiltering传入的值就是ACTV输入框中的输入的文字

  • 4 performFiltering返回根据关键字匹配到的数据,再将返回数据传入publishResults。

  • 5 最后通知ACTV数据过滤工作结束(onFilterComplete), ACTV在onFilterComplete中展示候选列表窗口。

最后

以上就是整齐发夹为你收集整理的AutoComleteTextView最佳实践-原理剖析篇AutoComleteTextView最佳实践-原理剖析篇的全部内容,希望文章能够帮你解决AutoComleteTextView最佳实践-原理剖析篇AutoComleteTextView最佳实践-原理剖析篇所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(36)

评论列表共有 0 条评论

立即
投稿
返回
顶部