目标:自定义ListView项布局通常需要自己实现Adapter,并通过搜索关键字筛选部分数据。且关键字变长变短,甚至为空时都应该正确搜索。
关键字:ListView Adapter ViewHolder Filter
最终实现如下效果:
借鉴了几篇资料后终于弄好了一个带过滤器的数据adapter。网上要一次性弄全资料还挺困难的,主要借鉴:
http://www.cnblogs.com/mengdd/p/3254323.html (Adapter中ViewHolder的使用)
http://www.oschina.net/code/snippet_1021353_35874 (Adapter实现Filterable。有问题)
http://stackoverflow.com/questions/25458519/how-to-implement-filterable-on-a-baseadapter(Adapter实现Filterable。正解)
要点:
1、继承BaseAdapter,getView()方法要使用ViewHolder方式减少实例化view
2、实现Filterable接口,要注意保留原始数据(上述两篇实现Filterable文章的区别)
3、synchronized 同步,效果实现,先不深究了
上代码:其中VoStation是我自定义的实体类。
</pre><pre>
public class VoStationAdapter extends BaseAdapter implements Filterable { // 适配器的当前数据 private ArrayList<VoStation> _data; // 适配器的原始数据 private List<VoStation> _originalData; // 自定义的过滤器 private SearchFilter _filter; private LayoutInflater _inflater; private final Object _lock = new Object();// 同步锁?不太懂 /** * 构造函数 * * @param context * 上下文 * @param data * 适配器数据 */ public VoStationAdapter(Context context, ArrayList<VoStation> data) { _inflater = LayoutInflater.from(context); _data = data; } /** * 【见备注1】重设原始数据 在原始数据发生变化时使用 **/ public void resetData(ArrayList<VoStation> data) { _data = data; if (_originalData != null) _originalData = _data; } /** 获得数据过滤器 */ public Filter getFilter() { if (_filter == null) { _filter = new SearchFilter(); } return _filter; } @Override public int getCount() { return _data.size(); } @Override public VoStation getItem(int position) { return _data.get(position); } @Override public long getItemId(int position) { // <span style="font-family: Arial, Helvetica, sans-serif;">【见备注2】</span><span style="font-family: Arial, Helvetica, sans-serif;">自定义ID</span> // 在此最好返回数据的唯一标识,在一些特定情况下使用到 // 如果没有,此处一般返回position return _data.get(position).getID(); } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = _inflater.inflate(R.layout.listitem_bill, parent, false);// 注意参数 holder.title = (TextView) convertView.findViewById(R.id.tvTitle); holder.text = (TextView) convertView.findViewById(R.id.tvText); holder.time = (TextView) convertView.findViewById(R.id.tvDate); holder.image = (ImageView) convertView.findViewById(R.id.ivIcon); // convertView.setTag(holder);//【见备注3】我还需要绑定数据,若不绑定,使用本行代替下行 convertView.setTag(R.id.tag1, holder); } else { holder = (ViewHolder) convertView.getTag(R.id.tag1); } holder.title.setText(_data.get(position).getName()); holder.text.setText("基站很长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长"); holder.time.setText(String.format("%s,%s", _data.get(position).getLon(), _data.get(position).getLat())); holder.image.setImageResource(R.drawable.item); convertView.setTag(R.id.tagVO, _data.get(position));// 【见备注3】绑定数据 return convertView; } class ViewHolder { ImageView image; TextView title; TextView text; TextView time; } // 内部类:数据过滤器 class SearchFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { // 定义过滤规则 FilterResults filterResults = new FilterResults(); // 保存原始数据 if (_originalData == null) { synchronized (_lock) { _originalData = new ArrayList<VoStation>(_data); } } // 如果搜索框内容为空,就恢复原始数据 if (TextUtils.isEmpty(constraint)) { synchronized (_lock) { filterResults.values = _originalData; filterResults.count = _originalData.size(); } } else { // 否则过滤出新数据 String filterString = constraint.toString().trim() .toLowerCase(Locale.US);// 过滤首尾空白,小写过滤 ArrayList<VoStation> newValues = new ArrayList<VoStation>(); for (VoStation vo : _originalData) { if (vo.getName().toLowerCase(Locale.US) .contains(filterString)) { newValues.add(vo); } filterResults.values = newValues; filterResults.count = newValues.size(); } } return filterResults; } @SuppressWarnings("unchecked") @Override protected void publishResults(CharSequence constraint, FilterResults results) { _data = (ArrayList<VoStation>) results.values;// 更新适配器的数据 if (results.count > 0) { notifyDataSetChanged();// 通知数据发生了改变 } else { notifyDataSetInvalidated();// 通知数据失效 } } } }
备注2:关于 adapter的getItemId()方法,见我另外一篇文章:
baseadapter.getItemId的使用方法:实现listview筛选、动态删除
备注3:此处仅为新手解释一下,大神肯定都知道什么意思。如果只绑定一个数据,使用convertView.setTag(holder)即可。但我想试试为每个列表项的view绑定对应的实体类对象,所以,此处我需要绑定两个数据,一个是holder,一个是实体类对象vo。因此使用convertView.setTag(int,object)方法,第一个参数是用资源文件定义的一个ID,第二个是绑定的对象。在需要的地方,我可以这么使用:
String s1 = "ViewTagVo:" + ((VoStation) view.getTag(R.id.tagVO)).getName();
定义好adapter后,在需要过滤数据时的调用:
ArrayList<VoStation> data = new ArrayList<VoStation>(); data.add(vo);//添加数据... VoStationAdapter adapter = new VoStationAdapter(this, data); lvContent.setAdapter(adapter);//lvContent是ListView组件 adapter.getFilter().filter("过滤关键字");