使用Adapter结合Filter做过滤的时候,在分别继承ArrayAdapter和BaseAdapter时遇到“想修改数据而错误修改了引用”的经典问题。记录遇到的详细情况以免再犯。
继承ArrayAdapter:
private class MyAdapter extends ArrayAdapter<String> { private Context mContext; private int mResource; private List<String> mData; private MyFilter mFilter; public MyAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<String> objects) { //这里会将object赋值给父类的mObjects成员变量,问题的所在 super(context, resource, objects); this.mContext = context; this.mResource = resource; this.mData = objects; } @Override public int getCount() { return mData.size(); } @Nullable @Override public String getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { View view; if (convertView == null) { view = LayoutInflater.from(mContext).inflate(mResource, parent, false); } else { view = convertView; } TextView text = (TextView) view.findViewById(android.R.id.text1); text.setText(mData.get(position)); return view; } @NonNull @Override public Filter getFilter() { if (mFilter == null) { mFilter = new MyFilter(); } return mFilter; } private class MyFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { String filterString = constraint.toString().toLowerCase(); FilterResults results = new FilterResults(); //为null,表示没有赋值过,这里的逻辑是mOriginalValues保存原始数据,而mData保存过滤后的数据 if (mOriginalValues == null) { mOriginalValues = new ArrayList<>(mData); } if (TextUtils.isEmpty(filterString)) { results.values = mOriginalValues; results.count = mOriginalValues.size(); } else { List<String> values = new ArrayList<>(mOriginalValues); List<String> newValues = new ArrayList<>(); for (int i = 0; i < values.size(); i++) { String value = values.get(i); if (value.contains(filterString)) { newValues.add(value); } } results.values = newValues; results.count = newValues.size(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { //mData.clear(); //mData.addAll((List<String>)results.values); //noinspection unchecked mData = (List<String>) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } } }
继承BaseAdapter:
private class NewAdapter extends BaseAdapter{ private Context mContext; private int mResource; private List<String> mList; private ArrayFilter mFilter; public NewAdapter(Context context, int resource, List<String> list) { this.mContext = context; this.mList = list; this.mResource = resource; } @Override public int getCount() { return 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 ; if (convertView == null){ view = LayoutInflater.from(mContext).inflate(mResource, null, false); }else{ view = convertView; } ((TextView)view).setText((String)getItem(position)); return view; } public ArrayFilter getFilter(){ if (mFilter == null){ mFilter = new ArrayFilter(); } return mFilter; } private class ArrayFilter extends Filter{ @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); String filterString = constraint.toString().toLowerCase(); if (mOriginalValues == null){ mOriginalValues = new ArrayList<>(mList); } if (TextUtils.isEmpty(filterString)){ results.values = new ArrayList<>(mOriginalValues); results.count = mOriginalValues.size(); }else{ List<String> values = new ArrayList<>(mOriginalValues); List<String> newValues = new ArrayList<>(); for (int i = 0; i < values.size(); i++) { String value = values.get(i).toLowerCase(); if (value.contains(filterString)){ newValues.add(value); } } results.values = newValues; results.count = newValues.size(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { //noinspection unchecked mList = (List<String>)results.values; if (results.count > 0){ notifyDataSetChanged(); }else{ notifyDataSetInvalidated(); } } } }
问题描述:
继承BaseAdapter的时候必须重写getItem、getCount、getItemId等几个方法,而继承ArrayAdapter的时候只必须有父类相应参数列表的构造方法。我一开始习惯使用BaseAdapter,后来发现直接继承ArrayAdapter代码更简洁。然后在结合使用Filter的时候出现了问题。
问题:修改数据集合后notifyDataSetChanged没有改变。
例如(参照示例片段):
1、重写ArrayAdapter的时候必须实现父类的构造方法(问题所在)。
2、执行new MyAdapter(this,resourceId, datas);
3、那么datas会最终赋值给ArrayAdapter的mObjects成员变量
4、此时MyAdapter中的mData和mObjects指向同一块数据
5、如果没有重写getCount,则getCount=mObjects.size();
6、如果重写了getCount(如下),则getCount=mData.size();
7、在使用Filter之后,修改mData指向过滤后的数据,然而mObjects并没有改变
8、可是这里决定ListView数据集的是mObjects引用,并没有相应更新
解决方法:
1、修改数据引用变量mData的同时,修改mObjects。
2、只使用mData,不使用mObjects(倒不如直接继承BaseAdapter逻辑更清晰)
2、直接修改指向的数据集。mData.clear();mData.addAll()。
示例代码片段:
//重写getCount、getItem等方法,使用mData引用 public int getCount() { return mData.size(); } public MyAdapter(@NonNull Context context, @LayoutResint resource, @NonNull List<String> objects) { //这个父类构造方法会将objects保存到mObjects,作为数据集的真正引用 super(context, resource, objects); this.mData = objects; } public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @IdRes int textViewResourceId, @NonNull List<T> objects) { mContext = context; mInflater = LayoutInflater.from(context); mResource = mDropDownResource = resource; mObjects = objects;//如果要修改数据引用,那么应该修改mObjects,而不是mData mFieldId = textViewResourceId; } //直接修改数据的方式而不是修改引用变量: @Override protected void publishResults(CharSequence constraint, FilterResults results) { //noinspection unchecked //mList = (List<String>) results.values; mData.clear(); mData.addAll((List<String>)results.values);//直接修改引用的数据,而非引用本身 if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } }
总结:
无搜索功能的ListView
1、继承ArrayAdapter代码更简洁
具备搜索功能的ListView
1、继承BaseAdapter
2、继承ArrayAdapter并重写getItem、getCount等方法使用本类的引用变量
3、继承ArrayAdapter,不改引用变量,直接修改数据集
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。