显示空视图后过滤器列表不会重新出现
Filter list not reappearing after empty view shown
出于某种原因,在显示没有匹配项目的空视图后,我的列表视图在从搜索视图中删除所有文本后不会重新出现。
Within my adapter class, AFAIK .clear()
in mData.clear();
needs to change to something else but I don't know what to.
ItemListAdapter class
public class ItemListAdapter extends BaseAdapter implements Filterable {
private List<Victoria> mData;
private List<Victoria> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<Victoria> data, Context context) {
mData = data;
mFilteredData = new ArrayList(mData);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return mFilteredData.size();
}
@Override
public String getItem(int position) {
return mFilteredData.get(position).getItem();
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.item_title);
holder.description = (TextView) convertView.findViewById(R.id.item_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(mFilteredData.get(position).getItem());
holder.description.setText(mFilteredData.get(position).getItemDescription());
return convertView;
}
@Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView title;
private TextView description;
}
/**
* Filter for filtering list items
*/
/**
* <p>An array filter constrains the content of the array adapter with
* a prefix. Each item that does not start with the supplied prefix
* is removed from the list.</p>
*/
private class ItemFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = mData;
} else {
//Create a new list to filter on
List<Victoria> resultList = new ArrayList<Victoria>();
for (Victoria str : mData) {
if (str.getItemDescription().toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(str);
}
}
results.count = resultList.size();
results.values = resultList;
}
return results;
}
/**
* Runs on ui thread
* @param constraint the constraint used for the result
* @param results the results to display
*/
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
//Make list invisible
//Make text view visible
mFilteredData.clear();
notifyDataSetInvalidated();
} else {
mFilteredData = (ArrayList<Victoria>)results.values;
notifyDataSetChanged();
}
}
}
}
过滤前
使用无效字符过滤后
清除搜索视图并再次尝试显示列表后
您正在修改包含原始数据的数组,当de Adapter
重新创建列表时,该数组没有数据。
正确的做法是创建原始数组的副本,return每次过滤列表时都将副本作为结果,保持原始数据不变。在 ArrayAdapter
实现中可以找到一个很好的例子。见源码here.
为了以防万一,我将在此处包含源代码的副本:
/**
* <p>An array filter constrains the content of the array adapter with
* a prefix. Each item that does not start with the supplied prefix
* is removed from the list.</p>
*/
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<T>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<T>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<T>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final String valueText = value.toString().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
一些小的修复应该会让事情适合你。首先,mData
和 mFilteredData
数据应该始终是两个不同的实例。在构造函数中,让 mFilteredData
成为从 mData
复制的新实例
mFilteredData = new ArrayList(mData);
另外在Filter
中,您需要更新相应的代码如下:
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = new ArrayList(mData);
}
那么您的 getView()
方法应该从 mFilteredData
而不是 mData
中提取。但理想情况下,getView()
方法应该使用 getItem(position)
方法来访问数据。这样,如果您更改从何处访问数据的实现细节,则不必记得单独更新 getView()
方法。
最后,作为旁注,您的过滤代码之所以有效,只是因为适配器没有改变 mFilteredData
或 mData
。通常,必须大量 synchronize
过滤过程和适配器的其他部分,因为 performFiltering()
发生在后台线程上。如果您计划在构建后添加对修改适配器数据的支持...那么您需要开始添加 synchronized
块。
出于某种原因,在显示没有匹配项目的空视图后,我的列表视图在从搜索视图中删除所有文本后不会重新出现。
Within my adapter class, AFAIK
.clear()
inmData.clear();
needs to change to something else but I don't know what to.
ItemListAdapter class
public class ItemListAdapter extends BaseAdapter implements Filterable {
private List<Victoria> mData;
private List<Victoria> mFilteredData;
private LayoutInflater mInflater;
private ItemFilter mFilter;
public ItemListAdapter (List<Victoria> data, Context context) {
mData = data;
mFilteredData = new ArrayList(mData);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return mFilteredData.size();
}
@Override
public String getItem(int position) {
return mFilteredData.get(position).getItem();
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.item_title);
holder.description = (TextView) convertView.findViewById(R.id.item_description);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(mFilteredData.get(position).getItem());
holder.description.setText(mFilteredData.get(position).getItemDescription());
return convertView;
}
@Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ItemFilter();
}
return mFilter;
}
/**
* View holder
*/
static class ViewHolder {
private TextView title;
private TextView description;
}
/**
* Filter for filtering list items
*/
/**
* <p>An array filter constrains the content of the array adapter with
* a prefix. Each item that does not start with the supplied prefix
* is removed from the list.</p>
*/
private class ItemFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = mData;
} else {
//Create a new list to filter on
List<Victoria> resultList = new ArrayList<Victoria>();
for (Victoria str : mData) {
if (str.getItemDescription().toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(str);
}
}
results.count = resultList.size();
results.values = resultList;
}
return results;
}
/**
* Runs on ui thread
* @param constraint the constraint used for the result
* @param results the results to display
*/
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
//Make list invisible
//Make text view visible
mFilteredData.clear();
notifyDataSetInvalidated();
} else {
mFilteredData = (ArrayList<Victoria>)results.values;
notifyDataSetChanged();
}
}
}
}
过滤前
使用无效字符过滤后
清除搜索视图并再次尝试显示列表后
您正在修改包含原始数据的数组,当de Adapter
重新创建列表时,该数组没有数据。
正确的做法是创建原始数组的副本,return每次过滤列表时都将副本作为结果,保持原始数据不变。在 ArrayAdapter
实现中可以找到一个很好的例子。见源码here.
为了以防万一,我将在此处包含源代码的副本:
/**
* <p>An array filter constrains the content of the array adapter with
* a prefix. Each item that does not start with the supplied prefix
* is removed from the list.</p>
*/
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<T>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<T>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<T>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final String valueText = value.toString().toLowerCase();
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
一些小的修复应该会让事情适合你。首先,mData
和 mFilteredData
数据应该始终是两个不同的实例。在构造函数中,让 mFilteredData
成为从 mData
mFilteredData = new ArrayList(mData);
另外在Filter
中,您需要更新相应的代码如下:
if (TextUtils.isEmpty(constraint)) {
results.count = mData.size();
results.values = new ArrayList(mData);
}
那么您的 getView()
方法应该从 mFilteredData
而不是 mData
中提取。但理想情况下,getView()
方法应该使用 getItem(position)
方法来访问数据。这样,如果您更改从何处访问数据的实现细节,则不必记得单独更新 getView()
方法。
最后,作为旁注,您的过滤代码之所以有效,只是因为适配器没有改变 mFilteredData
或 mData
。通常,必须大量 synchronize
过滤过程和适配器的其他部分,因为 performFiltering()
发生在后台线程上。如果您计划在构建后添加对修改适配器数据的支持...那么您需要开始添加 synchronized
块。