Android:使用AppCompatAutoCompleteTextView

我们先看看实现的效果吧
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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。。。如果不考虑兼容问题的话,实现起来应该不算太难。

猜你喜欢

转载自blog.csdn.net/new_Aiden/article/details/53732000