我们先看看实现的效果吧
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MUbDEIhT-1614134109259)(https://img-blog.csdn.net/20161219001816589?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmV3X0FpZGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-exmfZ1S3-1614134109262)(https://img-blog.csdn.net/20161219001827886?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmV3X0FpZGVu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]
也就是我们俗话说的自动提示功能。这里我实现了点击AppCompatAutoCompleteTextView就会弹出提示框,以及不输入内容时也显示提示框。这里主要是熟悉用法。还有很多很多api,请大家自行了解。。。
因为例子中提示的内容是Language,所以就建立类了
Language.java
public class Language {
public String name;
public int icon;
}
子项的布局文件item.xml。这里就显得很简单了。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:gravity="center_vertical"
android:layout_weight="1"
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="50dp" />
<ImageView
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:id="@+id/icon"/>
</LinearLayout>
Activity布局文件activity_main.xml
<com.test.MyAutoCompleteTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_test"
android:layout_width="match_parent"
android:layout_height="80dp"
android:dropDownHeight="200dp"
android:hint="language" />
为什么需要继承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 ArrayList<Language> languages;
// 没有经过过滤的数组
private ArrayList<Language> origin;
private Context context;
private int layoutId;
核心变量是languages和origin。
构造函数就是依次为这些变量赋值。
public MyAdapter(Context context, int resource, ArrayList<Language> languages) {
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();
List<Language> temp = 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<Language>) results.values;
// 更新视图
notifyDataSetChanged();
}
}
这下简单了,getFilter()方法返回一个MyFilter对象就行了。
public Filter getFilter() {
return new MyFilter();
}
好了,上述的话,MyAdapter就实现完毕了。
那我们可以在Activity中使用了。
MainActivity.java
// 初始化数据
final ArrayList<Language> languages = 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 = myAdapter.getItem(position);
textView.setText(language.name);
}
});
最后说道,这里为什么要用到AppCompatAutoCompleteTextView,而不是AutoCompleteTextView。因为AppCompatAutoCompleteTextView更能兼容好版本的Android系统,所以就使用AppCompatAutoCompleteTextView
至于AppCompatAutoCompleteTextView还有其他强大的功能,就需要自己去探索了,会了基本用法,其他的应该不难。
补充一下,其实AppCompatAutoCompleteTextView内部有一个ListPopupWindow,提示的内容就是用ListPopupWindow实现的。而ListPopupWindow我们可以理解为是ListView+PopupWindow。其实也就是说,我们可以自己实现AppCompatAutoCompleteTextView,自定义一个View,里面是一个输入框和PopupWindow,PopupWindow里面又有ListView。。。如果不考虑兼容问题的话,实现起来应该不算太难。