Android:使用AppCompatAutoCompleteTextView:我们先看看实现的效果吧,也就是我们俗话说的自动提示功能。这里我实现了点击AppCompatAutoCompleteTextView就会弹出提示框,以及不输入内容时也显示提示框。这里主要是熟悉用法。还有很多很多api,请大家自行了解。
因为例子中提示的内容是Language,所以就建立类了
Language.java
public class Language { public String name; public int icon; }
子项的布局文件item.xml。这里就显得很简单了。
Activity布局文件activity_main.xml
为什么需要继承AppCompatAutoCompleteTextView呢?是因为系统自带的AppCompatAutoCompleteTextView给提示的时候,输入框的内容长度至少要大于1。但是我们有时候是需要不输入内容就给出提示的,所以就需要重写AppCompatAutoCompleteTextView。
MyAutoCompleteTextView .java
public class MyAutoCompleteTextView extends AppCompatAutoCompleteTextView { public MyAutoCompleteTextView(Context context) { super(context); } public MyAutoCompleteTextView(Context context, AttributeSet attrs) { super(context, attrs); } public MyAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean enoughToFilter() { return true; } }
其实又显示MyAutoCompleteTextView 类有点冗余了,也就是重写了enoughToFilter()方法,一直返回true。有兴趣的,可以看看自带的AutoCompleteTextView 返回的是什么。
接着我们为MyAutoCompleteTextView写Adapter了。这里直接使用ArrayAdapter好了。其实如果没有特殊需求,ArrayAdapter能满足我们很大的需求。其实上述功能肯定是需要用到Filter的(过滤功能)。而ArrayAdapter已经申明了实现Filterable接口了。不过为了更好的理解功能,我还是声明实现Filterable接口。
MyAdapter.java
首先需要声明几个变量
// 经过过滤的数组 private ArrayListlanguages; // 没有经过过滤的数组 private ArrayList origin; private Context context; private int layoutId;
核心变量是languages和origin。
构造函数就是依次为这些变量赋值。
public MyAdapter(Context context, int resource, ArrayListlanguages) { super(context, resource); this.languages = languages; this.origin = languages; this.context = context; this.layoutId = resource; }
这里最好重写两个方法,一个是getItem(),另一个是getCount()。如果不写,可能会导致不显示
@Override public Language getItem(int position) { return languages == null ? null : languages.get(position); } @Override public int getCount() { return languages == null ? 0 : languages.size(); }
为了实现缓存,我们肯定是要写getView方法的。所以我们得先定义viewHolder,作为MyAdapter的内部类
private class ViewHolder { TextView name; ImageView icon; }
好了,写getView()
@NonNull @Override public View getView(int position, View convertView, @NonNull ViewGroup parent) { ViewHolder holder; if(null == convertView) { convertView = LayoutInflater.from(context).inflate(layoutId, parent, false); holder = new ViewHolder(); holder.name = (TextView) convertView.findViewById(R.id.name); holder.icon = (ImageView) convertView.findViewById(R.id.icon); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.name.setText(languages.get(position).name); holder.icon.setBackgroundResource(languages.get(position).icon); return convertView; }
通过view自带的setTag和getTag方法实现view的复用。
前面说到,MyAdapter的声明是这样的
public class MyAdapter extends ArrayAdapter implements Filterable
要就要重写getFilter()方法了。
我们先自定义Filter
private class MyFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); if(null == constraint || 0 == constraint.length()) { constraint = ""; } // 这里做一些简单的过滤 String condition = String.valueOf(constraint).toLowerCase(); Listtemp = new ArrayList<>(); for (Language language : origin) { if (language.name.toLowerCase().contains(condition)) { temp.add(language); } } results.values = temp; results.count = temp.size(); // 返回的results会在publishResult()函数中得到 return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { languages = (ArrayList ) results.values; // 更新视图 notifyDataSetChanged(); } }
这下简单了,getFilter()方法返回一个MyFilter对象就行了。
public Filter getFilter() { return new MyFilter(); }
好了,上述的话,MyAdapter就实现完毕了。
那我们可以在Activity中使用了。
MainActivity.java
// 初始化数据 final ArrayListlanguages = new ArrayList<>(); Language one = new Language(); one.name = "Java"; one.icon = R.mipmap.java; languages.add(one); Language two = new Language(); two.name = "c"; two.icon = R.mipmap.c; languages.add(two); Language three = new Language(); three.name = "Python"; three.icon = R.mipmap.python; languages.add(three); Language four = new Language(); four.name = "gradle"; four.icon = R.mipmap.gradle; languages.add(four); Language five = new Language(); five.name = "php"; five.icon = R.mipmap.php; languages.add(five); Language six = new Language(); six.name = "groovy"; six.icon = R.mipmap.groovy; languages.add(six);
上述的图标都是自己下载的,就随意下载了一些。
使用Adapter
MyAdapter myAdapter = new MyAdapter(this, R.layout.item, languages); final MyAutoCompleteTextView textView = (MyAutoCompleteTextView) this.findViewById(R.id.tv_test); textView.setAdapter(myAdapter);
为了进一步改善交互过程,点击MyAutoCompleteTextView也应该弹出提示内容才行。
textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { textView.showDropDown(); } });
并且,点击了MyAutoCompleteTextView的子项也应该要显示出来吧?值得开心的是MyAutoCompleteTextView有setOnItemClickListener方法,有点类似于ListView
textView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { Language language = languages.get(position); textView.setText(language.name); } });
最后说道,这里为什么要用到AppCompatAutoCompleteTextView,而不是AutoCompleteTextView。因为AppCompatAutoCompleteTextView更能兼容好版本的Android系统,所以就使用AppCompatAutoCompleteTextView
至于AppCompatAutoCompleteTextView还有其他强大的功能,就需要自己去探索了,会了基本用法,其他的应该不难。
补充一下,其实AppCompatAutoCompleteTextView内部有一个ListPopupWindow,提示的内容就是用ListPopupWindow实现的。而ListPopupWindow我们可以理解为是ListView+PopupWindow。其实也就是说,我们可以自己实现AppCompatAutoCompleteTextView,自定义一个View,里面是一个输入框和PopupWindow,PopupWindow里面又有ListView。如果不考虑兼容问题的话,实现起来应该不算太难。