Android AutoCompleteTextView 源码解析

在研究邮箱后缀自动补齐的时候,好奇那几个方法的调用逻辑,就研究了源码是怎么调用的.

内容输入
  • 当用户在 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;
    }
    
  • 看看DropDownItemClickListeneronItemClick().
    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());
    }
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值