上班的第一天没什么活儿,就看了看这个控件的源码来验证一下联想搜索列表的实现方法是否是自己所想的那样,看完了才发现果然如此!第一天上班就不详细的写了,在这里简单的写一下联想搜索下拉菜单的实现原理。细节方面以后再另写博文补上。
首先我们要知道一个事实:联想搜索列表是一个悬浮框,我之前的博文也有提过既然是悬浮框肯定少不了WindowManager这个东东的身影(详见《WindowManager杂谈》),这是看到悬浮框的等类似功能界面的时候我们应该首先能想到的!!!在android开发过程中常见的WindowManager就是Dialog(AlertDialog)(详见《AlertDialog创建过程详解》或者PopupWindow了(详见《PopupWindow简单说明》)。
在详细说明之前写把AutoCompleteTextView的既定事实列出来:
1)联想搜索提示列表这个悬浮框是通过PopupWindow实现的
2)PopupWindow可以通过setContentView来说实现用户自定义的界面,所以根据AutoCompleteTextView的运行效果不难猜出 PopupWindow里面是一个ListView
3)根据1)和2)的说明AutoCompleteTextView核心原理整体上就是PopupWindow+ListView的结合。
这点从它的源码中体现出来:
//AutoCompleteTextView.java
public class AutoCompleteTextView extends EditText implements Filter.FilterListener {
//该变量在AutoCompleteTextView的构造器中得到初始化
private ListPopupWindow mPopup;
//具体交给ListPopupWindow的mAdapter变量使用
private ListAdapter mAdapter;
}
//ListPopupWindow .java
public class ListPopupWindow {
//该对象在ListPopupWindow进行了初始化。
private PopupWindow mPopup;
private ListAdapter mAdapter;
private DropDownListView mDropDownList;
}
//AutoCompleteTextView的setAdapter方法。
public void setAdapter(T adapter) {
//此处有省略代码
//交给ListPopupWindow的setAdapte方法来处理
mPopup.setAdapter(mAdapter);
}
//ListPopupWindow的setAdapter方法
public void setAdapter(ListAdapter adapter) {
//此处有省略代码
//注意这里对mDropDownList进行了非空判断
if (mDropDownList != null) {
mDropDownList.setAdapter(mAdapter);
}
}
读到这儿应该不难理解上面的三条结论的正确性!需要注意的是在ListPopupWindow这个类的构造器里面完成了对PopupWindow的初始化,并没有对ListView进行初始化,这也是为什么在setAdapter对mDropDownList这个ListView进行非空判断的原因。
那么什么时候对mDropDownList 进行初始化呢?答案是在buildDropDown()这个ListPopupWindow类的private方法里,该方法在ListPopupWindow的show方法里面调用。也就是在显示搜索提示列表的时候才初始化,这点倒是跟Dialog有点类似。Dialog也是在第一次调用show方法的时候完成了页面布局的初始化,达到延迟初始化的效果,先简单的分析一下buildDropDown方法都做了写什么:
private int buildDropDown() {
ViewGroup dropDownView;
int otherHeights = 0;
//如果是第一次调用show的时候
if (mDropDownList == null) {
//此处有省略代码
//初始化ListView
mDropDownList = new DropDownListView(context, !mModal);
mDropDownList.setAdapter(mAdapter);
//设置item点击事件,这个在AutoCompleteTextView初始化ListPopupWindow的是时候进行了设置
mDropDownList.setOnItemClickListener(mItemClickListener);
mDropDownList.setFocusable(true);
mDropDownList.setFocusableInTouchMode(true);
//初始化dropDownView
dropDownView = mDropDownList;
//mPromptView为一个TextView,在AutoCompleteTextView调用setCompletionHint调用的时候初始化
View hintView = mPromptView;
if (hintView != null) {
//线性布局
LinearLayout hintContainer = new LinearLayout(context);
//设置为垂直布局
hintContainer.setOrientation(LinearLayout.VERTICAL);
//设置线性布局的布局参数
LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f
);
//提示信息的上下位置
switch (mPromptPosition) {
case POSITION_PROMPT_BELOW://让提示信息显示在ListView下面
hintContainer.addView(dropDownView, hintParams);
hintContainer.addView(hintView);
break;
case POSITION_PROMPT_ABOVE://让提示信息显示在ListView上满
hintContainer.addView(hintView);
hintContainer.addView(dropDownView, hintParams);
break;
}
//此处有省略代码
dropDownView = hintContainer;
}
//这个是关键为PopupWindow设置了页面布局
mPopup.setContentView(dropDownView);
} else {//如果已经初始化了dropDownView或者mDropDownList
dropDownView = (ViewGroup) mPopup.getContentView();
final View view = mPromptView;
if (view != null) {
LinearLayout.LayoutParams hintParams =
(LinearLayout.LayoutParams) view.getLayoutParams();
otherHeights = view.getMeasuredHeight() + hintParams.topMargin
+ hintParams.bottomMargin;
}
}
//此处省略了部分代码
。。。
}
1)初始化ListView
2)调用PopupWindowsetContentView来完成了悬浮框页面的初始化。
注意如果你在使用AutoCompleteTextView的时候调用了setCompletionHint这个方法来设置提示信息,那么ListView就会被添加到一个LinearLayout里面,并将这个LinearLayout
作为popWindow的contentView.否则直接将ListView作为PopupWindow的contentView。
此时页面中并没有显示出来,调用了ListPopupWindow类的show方法的时候才真正显示出来,show方法的主要逻辑如下伪代码:
if(isShow){
mPopup.update();
}else{
mPopup.showAsDropDown();
}
如果已经搜索提示列表显示出来了,就调用PopupWindow的update方法,如果还没有显示出来就调用shwoAsDropDown方法。
到此为止AutoCompleteTextView的悬浮提示列表的创建过程就梳理完了,如有不正确的地方,欢迎批评指正。