在研究邮箱后缀自动补齐的时候,好奇那几个方法的调用逻辑,就研究了源码是怎么调用的.
内容输入
- 当用户在
AutoCompleteTextView
控件中输入内容时候,会触发自身的afterTextChanged()
public void afterTextChanged(Editable s) { ... //关注该方法 refreshAutoCompleteResults(); } public final void refreshAutoCompleteResults() { // the drop down is shown only when a minimum number of characters // was typed in the text view if (enoughToFilter()) { ... //这个方法很重要,getText()为用户输入的内容 performFiltering(getText(), mLastKeyCode); } else { ... } } protected void performFiltering(CharSequence text, int keyCode) { mFilter.filter(text, this); }
开始过滤
- 看到
filter()
方法会好奇mFilter
对象是什么.当为AutoCompleteTextView
设置Adapter
时候获取到的.public <T extends ListAdapter & Filterable> void setAdapter(T adapter) { ... if (mAdapter != null) { //此处获取到mFilter对象,去ArrayAdapter中看看getFilter()方法做了什么 mFilter = ((Filterable) mAdapter).getFilter(); ... } else { mFilter = null; } mPopup.setAdapter(mAdapter); } //直接创建了一个ArrayFilter对象 @Override public @NonNull Filter getFilter() { if (mFilter == null) { mFilter = new ArrayFilter(); } return mFilter; }
- 看看
Filter
中的filter()
,这个方法主要是将用户输入的内容交由RequestHandler
中的handlerMessage()
处理public final void filter(CharSequence constraint, FilterListener listener) { mThreadHandler = new RequestHandler(thread.getLooper()); ... //用户输入的内容,交由 RequestHandler(Filter中的内部类)中的handlerMessage处理 args.constraint = constraint != null ? constraint.toString() : null; ... mThreadHandler.sendMessageDelayed(message, delay); } }
- 对于
RequestHandler
中的handlerMessage()
,我们需要分两步来看public void handleMessage(Message msg) { int what = msg.what; Message message; switch (what) { case FILTER_TOKEN: RequestArguments args = (RequestArguments) msg.obj; try { //这是第一步 //这里后看下去是个接口,回想上面的Filter对象是由ArrayAdapter里得方法提供,我们到时候看看ArrayAdapter中有没有什么发现, args.results = performFiltering(args.constraint); } catch (Exception e) { ... } finally { //这是第二步 //从ResultHandler获取一个Message message = mResultHandler.obtainMessage(what); message.obj = args; //将Message发送到ResultHandler中的 handlerMessage() message.sendToTarget(); ... break; } }
-
先看第一步,
ArrayAdapter
中的performFiltering()
,该方法接收的参数为用户输入的内容.protected FilterResults performFiltering(CharSequence prefix) { final FilterResults results = new FilterResults(); if (mOriginalValues == null) { synchronized (mLock) { //mObjects 看源码可知为创建ArrayAdapter时候传入的数组加工而来. //这里又重新复制一份数据. mOriginalValues = new ArrayList<>(mObjects); } } if (prefix == null || prefix.length() == 0) { ... //我们只分析输入内容不为空的情况 } else { final String prefixString = prefix.toString().toLowerCase(); final ArrayList<T> values; synchronized (mLock) { values = new ArrayList<>(mOriginalValues); } final int count = values.size(); //遍历创建ArrayAdapter时候传入的数据,用用户输入的内容作为前缀进行比对,相同的则用一个新的集合将这些数据存起来. final ArrayList<T> newValues = new ArrayList<>(); for (int i = 0; i < count; i++) { final T value = values.get(i); final String valueText = value.toString().toLowerCase(); if (valueText.startsWith(prefixString)) { //新集合存起来 newValues.add(value); } else { ... } } results.values = newValues; results.count = newValues.size(); } //最后将结果返回, 此时我们再回到上一步RequestHandler中的handlerMessage() return results; }
-
第二步,将过滤好的数据结果发送到
ResultsHandler
中的handlerMessage()
处理.@Override public void handleMessage(Message msg) { RequestArguments args = (RequestArguments) msg.obj; //第一步 publishResults(args.constraint, args.results); if (args.listener != null) { int count = args.results != null ? args.results.count : -1; //第二步 args.listener.onFilterComplete(count); } }
-
开始展示下拉列表
ResultsHandler
中的handlerMessage()
同样也需要分两步来看.- 第一步看看
publishResults()
,该方法同样是在ArrayAdapter中具体实现@Override protected void publishResults(CharSequence constraint, FilterResults results) { //一目了然, mObjects该变量为ArrayAdapter中得数据源,将过滤后的数据源替换原来得数据源. mObjects = (List<T>) results.values; //刷新数据 if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } }
- 第二步
onFilterComplete()
,最终会将ListPopupWindow
显示出来public void onFilterComplete(int count) { updateDropDownForFilter(count); } private void updateDropDownForFilter(int count) { ... if ((count > 0 || dropDownAlwaysVisible) && enoughToFilter) { if (hasFocus() && hasWindowFocus() && mPopupCanBeUpdated) { //显示出 showDropDown(); } } ... } public void showDropDown() { ... //最熟悉不过的show() mPopup.show(); mPopup.getListView().setOverScrollMode(View.OVER_SCROLL_ALWAYS); }
- 第一步看看
用户点击选择下拉列表中内容
目前,用户向
AutoCompleteTextView
输入内容,从过滤,到ListPopupWindow
(下拉列表)显示都分析过了, 最后就是点击ListPopupWindow
中的条目,条目中的内容如何展示到AutoCompleteTextView
中
- 看到在
AutoCompleteTextView
构造方法中为ListPopupWindow
对象设置了一个DropDownItemClickListener
对象public AutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes, Theme popupTheme) { super(context, attrs, defStyleAttr, defStyleRes); mPopup = new ListPopupWindow(mPopupContext, attrs, defStyleAttr, defStyleRes); mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW); mPopup.setListSelector(popupListSelector); //设置了一个条目点击的回调 mPopup.setOnItemClickListener(new DropDownItemClickListener()); }
- 看看回调方法是怎么被
ListPopupWindow
调用的//执行点击条目的方法 public boolean performItemClick(int position) { if (isShowing()) { if (mItemClickListener != null) { final DropDownListView list = mDropDownList; final View child = list.getChildAt(position - list.getFirstVisiblePosition()); final ListAdapter adapter = list.getAdapter(); mItemClickListener.onItemClick(list, child, position, adapter.getItemId(position)); } return true; } return false; }
- 看看
DropDownItemClickListener
的onItemClick()
.private class DropDownItemClickListener implements AdapterView.OnItemClickListener { public void onItemClick(AdapterView parent, View v, int position, long id) { //最终用户点击条目后,执行了该方法 performCompletion(v, position, id); } } private void performCompletion(View selectedView, int position, long id) { if (isPopupShowing()) { Object selectedItem; if (position < 0) { selectedItem = mPopup.getSelectedItem(); } else { //通过点击的条目获取到创建ArrayAdapter时候传入的数组中的某条数据 selectedItem = mAdapter.getItem(position); } if (selectedItem == null) { Log.w(TAG, "performCompletion: no selected item"); return; } mBlockCompletion = true; //看看这个方法,会将用户选中的条目数据传递给该方法. replaceText(convertSelectionToString(selectedItem)); ... } }
- 到了最后一步,就是将下拉列表中的数据展示到
AutoCompleteTextView
中,承接上一步的replaceText()
protected void replaceText(CharSequence text) { //清空数据 clearComposingText(); //将`ListPopupWindow`中被选中的条目所代表的数据展示到`AutoCompleteTextView`控件中 setText(text); Editable spannable = getText(); //处理下光标 Selection.setSelection(spannable, spannable.length()); }