項(xiàng)目需求是用戶登錄界面,輸入email的EditView里,彈出手機(jī)內(nèi)保存的account list,使用戶又可以手動(dòng)輸入,又可以直接從列表里選擇。
首先搜到一個(gè)很棒的實(shí)現(xiàn)方法:帶彈出列表的EditText。這篇博客嘗試了兩種方式實(shí)現(xiàn):ListPopupWindow和AutoCompleteTextView。博主先用了后者,說(shuō)特別是在橫屏切換的時(shí)候不方便,最后選擇了前者。自己真實(shí)嘗試了之后,才發(fā)現(xiàn)自己的需求反而是后者=。= (沒(méi)有好好理解博主意思的后果)
小白雖然源碼也讀的不是很明白,但AutoCompleteTextView的大概邏輯是:
watcher監(jiān)控->檢測(cè)到輸入變化->performingFilter從可選列表里過(guò)濾出與輸入匹配的list->彈出listPopupWindow
這樣一看就明白了,autoCompleteTextView比listPopupWindow多了只顯示匹配后的過(guò)程。估計(jì)那位博主沒(méi)有這樣的需求,只需要全部顯示list。而我們這邊的項(xiàng)目畫(huà)面又不需要橫屏,所以我們還是使用AutoCompleteTextView比較方便。
根據(jù)需求我們修改了原生代碼的兩個(gè)功能:
- 輸入內(nèi)容為空的時(shí)候也能彈出列表(源碼雖然有可以設(shè)置輸入長(zhǎng)度的threshold,但必須長(zhǎng)度大于0)
-->在調(diào)用部分增加onFocusChanged處理,且autoCompleteTextView里重載enoughToFilter()保證輸入為空也會(huì)filter更新列表。
- 內(nèi)容匹配不止是從頭開(kāi)始匹配,也實(shí)現(xiàn)中間的部分匹配
-->adapter里重載performingFilter()函數(shù)。匹配函數(shù)從原來(lái)的startwith(...)修改為contains(...)即可
重載AutoCompleteTextView:
public class InstantAutoCompleteTextView extends AutoCompleteTextView {
public InstantAutoCompleteTextView(Context context) {
super( context );
}
public InstantAutoCompleteTextView(Context context, AttributeSet attrs) {
super( context, attrs );
}
public InstantAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
super( context, attrs, defStyle );
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (focused && getAdapter() != null) {
performFiltering(getText(), KeyEvent.KEYCODE_UNKNOWN);
}
}
@Override
public boolean enoughToFilter() {
return true;
}
}
重載adapter部分,自定義performingFilter函數(shù):
public class AutoCompleteAdapter<T> extends ArrayAdapter<T> implements Filterable {
private List<T> listObjects;
List<T> suggestions = new ArrayList<>();
private Filter mFilter = new Filter(){
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if(constraint != null) {
suggestions.clear();
for(T object : listObjects){
if(object.toString().toLowerCase().contains(constraint.toString().toLowerCase())){
suggestions.add(object);
}
}
filterResults.values = suggestions;
filterResults.count = suggestions.size();
}
return filterResults;
}
@SuppressWarnings( "unchecked" )
@Override
protected void publishResults(CharSequence contraint, FilterResults results) {
if(results == null){
notifyDataSetInvalidated();
return;
}
List<T> filteredList = (List<T>) results.values;
if(results.count > 0) {
clear();
for (T filteredObject : filteredList) {
add(filteredObject);
}
notifyDataSetChanged();
}
}
};
public AutoCompleteAdapter(Context context, int resource, List<T> listObjects) {
super(context, resource, listObjects);
this.listObjects =new ArrayList<>( listObjects );
}
@Override
public Filter getFilter() {
return mFilter;
}
}
實(shí)際調(diào)用代碼片段:
public class MainActivity extends AppCompatActivity {
private InstantAutoCompleteTextView autoTextView;
private ArrayList<String> listArray;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initialize();
}
private void initialize(){
listArray=new ArrayList<>();
listArray.add("test 1");
listArray.add("test 2");
listArray.add("test 3");
listArray.add("default 9");
listArray.add("beautiful");
listArray.add("hello world");
listArray.add("boring sunday");
listArray.add("list test");
autoTextView=(InstantAutoCompleteTextView) findViewById(R.id.autoCompleteText);
AutoCompleteAdapter autocompleteAdapter = new AutoCompleteAdapter(this,android.R.layout.simple_list_item_1,listArray);
autoTextView.setAdapter(autocompleteAdapter);
}
}
最后test結(jié)果如下圖(應(yīng)用在項(xiàng)目里依然很完美?。?/p>

p.s. test的代碼:
https://github.com/bingningO/AutoCompleteTextViewTest