这两天公司做一个功能,
需要实现的就是在EditText中输入一个文本,然后根据输入的文本进行模糊查询,将结果放入到一个类似listview的列表中
最后通过点击列表中的条目,将数据展示到EditText中。
其实这个功能很久以前也有人讲过,此外针对这一功能google也有原生控件AutoCompeletTextView,
但是大部分的帖子用的也都是Google的ArrayAdapter。
如果是简简单单用单列字符串来实现,这个无可厚非,但是如果用到了复杂的item时,ArrayAdapter就表现的力不从心了。
因此自定义Adapter是十分必要的。
首先需要谢谢
Ricky大神的帖子,将我引领入门
也可以看一下原贴 http://blog.csdn.net/top_code/article/details/9326129
但是针对最后,整个项目中有一个小的bug
我所做的就是在前人的基础上做一下修改,以便为更多程序员带来一个更详细一点的使用方法
这里是主Activity
package com.autobyselfadapter;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AutoCompleteTextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends Activity implements OnItemClickListener {
List<PhoneContact> mList;
private AutoCompleteTextView mACTV;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buildAppData();
findView();
}
private void buildAppData() {
String[] names = { "abc", "allen", "bird", "bike", "book", "cray",
"david", "demon", "eclipse", "felling", "frank", "google",
"green", "hill", "hook","jin zhiwen", "jack", "jay", "king","kevin","kobe",
"lily", "lucy", "mike", "nike", "nail", "open","open cv",
"panda", "pp", "queue", "ray allen", "risk", "tim cook","T-MAC",
"tony allen",
"x man", "x phone", "yy", "world", "w3c", "zoom","zhu ziqing"};
mList = new ArrayList<PhoneContact>();
for (int i = 0; i < names.length; i++) {
PhoneContact pc = new PhoneContact(100 + i, names[i], "1861234567"
+ i, names[i].concat("@gmail.com"));
mList.add(pc);
}
}
private void findView() {
mACTV = (AutoCompleteTextView) findViewById(R.id.mACTV);
PhoneAdapter mAdapter = new PhoneAdapter(mList, getApplicationContext());
mACTV.setAdapter(mAdapter);
mACTV.setThreshold(1); //设置输入一个字符 提示,默认为2
mACTV.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// honeContact pc = mList.get(position);
//这里是原贴代码,当然也是bug的问题所在
//其取值是用原集合中获取的,因此不管我们搜索出结果展示的是什么,只要点击
//控件只会从原集合中按照索引进行选择,因此会出现例如输入x 会提示 x abc , x man ,然后我们点击第一个x abc但是展示的却是abc的数据
PhoneContact pc = ((PhoneAdapter)parent.getAdapter()).getFilteredList().get(position);
mACTV.setText(pc.getName()+" "+pc.getPhone());
}
}
这里是布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<AutoCompleteTextView
android:id="@+id/mACTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="" >
<requestFocus />
</AutoCompleteTextView>
</RelativeLayout>
在原贴中没有bean类,因此,这里添上
package com.autobyselfadapter;
/**
* Created by Administrator on 2016/11/20.
*/
public class PhoneContact {
private int id;
private String name;
private String email;
private String phone;
public void setEmail(String email) {
this.email = email;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public PhoneContact(int id, String name, String phone, String email) {
this.id = id;
this.name = name;
this.phone = phone;
this.email = email;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public String getPhone() {
return phone;
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="@+id/ivIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/app_name"
android:src="@mipmap/ic_launcher" />
<LinearLayout
android:id="@+id/appInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_toRightOf="@id/ivIcon"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="person_name"
android:textColor="#000000"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="phone"
android:textColor="#666666"
android:textSize="13sp" />
<TextView
android:id="@+id/tv_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="email"
android:textColor="#666666"
android:textSize="13sp" />
</LinearLayout>
<Button
android:id="@+id/btnClick"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:focusable="false"
android:text="call"
android:textColor="#000000"
android:textSize="16sp" />
</RelativeLayout>
下面是重点,也就是adapter的部分,
是继承了BaseAdapter,但是如果是给AutoCompleteTextView做适配
还必须要实现一个Filterable接口
下面会着重注释一下Filter的代码
package com.autobyselfadapter;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
public class PhoneAdapter extends BaseAdapter implements Filterable {
//全局变量Filter
private ArrayFilter mFilter;
//全局变量初始化的数据集合
private List<PhoneContact> mList;
//上下文
private Context context;
//未进行过滤的数据集合
private ArrayList<PhoneContact> mUnfilteredData;
//已经进行过滤了的数据集合
private ArrayList<PhoneContact> mfilteredData;
public PhoneAdapter(List<PhoneContact> mList, Context context) {
this.mList = mList;
this.context = context;
}
@Override
public int getCount() {
return mList==null ? 0:mList.size();
}
@Override
public Object getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder holder;
if(convertView==null){
view = View.inflate(context, R.layout.phone_item, null);
holder = new ViewHolder();
holder.tv_name = (TextView) view.findViewById(R.id.tv_name);
holder.tv_phone = (TextView) view.findViewById(R.id.tv_phone);
holder.tv_email = (TextView) view.findViewById(R.id.tv_email);
view.setTag(holder);
}else{
view = convertView;
holder = (ViewHolder) view.getTag();
}
PhoneContact pc = mList.get(position);
holder.tv_name.setText("姓名:"+pc.getName());
holder.tv_phone.setText("电话:"+pc.getPhone());
holder.tv_email.setText("Email:"+pc.getEmail());
return view;
}
static class ViewHolder{
public TextView tv_name;
public TextView tv_phone;
public TextView tv_email;
}
//getFilter方法就是实现了Filterable接口需要实现的方法
//此处必须要有一个过滤器实例化的返回值
@Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
public ArrayList<PhoneContact> getFilteredList()
{
return mfilteredData;
}
/**
* 自定义Filter(过滤器必须实现的两个方法 performFiltering 还有 publishResults )
*/
private class ArrayFilter extends Filter {
/**
* performFiltering 方法的作用是在子线程中运行,此外进行数据过滤
* 将符合条件的数据装入一个新的集合(新集合的作用就是存放符合条件)
* @param prefix 用户在控件中输入的字符
* @return 过滤结果集, 此处的返回值在publishResults方法中作为参数传入
*/
@Override
protected FilterResults performFiltering(CharSequence prefix) {
//初始化一个FilterResult
FilterResults results = new FilterResults();
//初始化未过滤集合,如果该集合未初始化,则进行初始化
if (mUnfilteredData == null) {
mUnfilteredData = new ArrayList<PhoneContact>(mList);
}
//对用户输入进行判断
if (prefix == null || prefix.length() == 0) {
//用户没有输入,数据不会产生变化
ArrayList<PhoneContact> list = mUnfilteredData;
results.values = list;
results.count = list.size();
} else {
//如果用户输入,或者是控件内存在字符
//将字符变为小写字符(后面叫做"查询字符串")
String prefixString = prefix.toString().toLowerCase();
//这一段总感觉没有用,不过支持原创,该句还是保留
ArrayList<PhoneContact> unfilteredValues = mUnfilteredData;
//获取总数据的长度(未过滤情况下,未过滤集合的长度就是总数据的长度)
int count = unfilteredValues.size();
//定义一个集合用户装过滤出来的数据(本人觉得这里的一句写的很好,直接定义出集合的大小
// 因此,不会出现集合长度不够,重新申请.或者是集合长度太大,造成内存浪费)
ArrayList<PhoneContact> newValues = new ArrayList<PhoneContact>(count);
//进行对未过滤数据循环遍历
for (int i = 0; i < count; i++) {
PhoneContact pc = unfilteredValues.get(i);
if (pc != null) {
if(pc.getName()!=null && pc.getName().startsWith(prefixString)){
/**进行判断如果用户名存在,并且用户名是用查询字符串开头,则符合条件*/
newValues.add(pc);
}else if(pc.getEmail()!=null && pc.getEmail().startsWith(prefixString)){
/**若上一条不符合,可以再用电子邮件进行判断,如果匹配,表示符合条件*/
newValues.add(pc);
}
}
}
//将过滤出来的数据传给全局变量过滤后数据
mfilteredData = newValues;
//对返回结果进行一个数据传递,分别是集合本身 还有集合长度
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
//FilterResults 的 返回值作为参数传递到这里
//可以通过results 获取到过滤后集合,然后传给作用于显示的集合
mList = (List<PhoneContact>) results.values;
//此外对集合元素数量进行判断
if (results.count > 0) {
//如果大于0,进行刷新,展示
notifyDataSetChanged();
} else {
//如果为0 则还原初始状态并且不会显示出来
notifyDataSetInvalidated();
}
}
}
}
与其说本文着重写AutoCompleteTextView,其实最重要的还是强调了一下关于Filter的写法,希望可以帮助到各位.