使用自定义(对象)适配器过滤 ListView

63
我正在尝试在使用自定义对象适配器的 ListView 中实现过滤功能,但是我找不到任何有用的示例。附带的代码非常简化,因此请注意我无法使用普通的 ArrayAdapter。我在 ListView 上方有一个 EditText,在用户在 EditText 中输入文本时,我想根据 EditText 中输入的文本来过滤 ListView。非常感谢任何建议!以下是来自活动类的代码片段:
public class management_objects extends Activity {

private static List<User> UserList;
private EfficientAdapter adapter = null;
private ListView objectListView = null;
private EditText SearchText = null;

private static class EfficientAdapter extends BaseAdapter implements Filterable{
    private LayoutInflater mInflater;   

    public EfficientAdapter(Context context) {
        mInflater = LayoutInflater.from(context);
    }

    public int getCount() {
        return UserList.size();
    }

    public Object getItem(int position) {
        return position;
    }

    public long getItemId(int position) {
        return position;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder; 
        if (convertView == null) { 
            convertView = mInflater.inflate(R.layout.imagelayout_2lines, null);
            holder = new ViewHolder();
            holder.text = (TextView) convertView.findViewById(R.id.managementObjectText);
            holder.subtext = (TextView) convertView.findViewById(R.id.managementObjectSubText);
            holder.icon = (ImageView) convertView.findViewById(R.id.managementObjectIcon);
            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.text.setText(UserList.get(position).getFirstName());
        holder.subtext.setText(UserList.get(position).getLastName());
        holder.icon.setImageResource(R.drawable.user);

        return convertView;
    }

    static class ViewHolder { 
        TextView text;
        TextView subtext;
        ImageView icon;
    }

    @Override
    public Filter getFilter() {
        return null;
    }
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.adobjectlist);
    Bundle extras = getIntent().getExtras();

    SearchText = (EditText) findViewById(R.id.SearchBox);    
    SearchText.addTextChangedListener(filterTextWatcher);

    objectListView = (ListView) findViewById(R.id.ObjectList);
    objectListView.setOnItemClickListener(Item_Click);
    adapter = new EfficientAdapter(this);
    ComputerName = extras.getString("COMPUTER_NAME");

    //Get User list from webservice
    ShowUsers();
}

这是用户类:

 public class User {
  private int UserId;
  private String FirstName;
  private String LastName;

    public int getUserId() {
        return UserId;
    }
    public void setUserId(int UserId) {
        this.UserId = UserId;
    }
    public String getFirstName() {
        return FirstName;
    }
    public void setFirstName(String FirstName) {
        this.FirstName = FirstName;
    }
    public String getLastName() {
        return LastName;
    }
    public void setLastName(String LastName) {
        this.LastName = LastName;
    }
}
4个回答

70
您需要执行几个操作:
1)在您的活动中,为包含用户输入值的EditText注册文本更改监听器:

mSearchValue.addTextChangedListener(searchTextWatcher);

2)创建searchTextWatcher并让它执行某些操作:
private TextWatcher searchTextWatcher = new TextWatcher() {
    @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // ignore
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // ignore
        }

        @Override
        public void afterTextChanged(Editable s) {
            Log.d(Constants.TAG, "*** Search value changed: " + s.toString());
            adapter.getFilter().filter(s.toString());
        }
    };

3) 在您的自定义适配器中覆盖getFilter()方法,并让其过滤结果并通知listview数据集已更改。

    @Override
    public Filter getFilter() {
        return new Filter() {
            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                Log.d(Constants.TAG, "**** PUBLISHING RESULTS for: " + constraint);
                myData = (List<MyDataType>) results.values;
                MyCustomAdapter.this.notifyDataSetChanged();
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                Log.d(Constants.TAG, "**** PERFORM FILTERING for: " + constraint);
                List<MyDataType> filteredResults = getFilteredResults(constraint);

                FilterResults results = new FilterResults();
                results.values = filteredResults;

                return results;
            }
        };
    }

4
为什么在第三个问题中要使用“override”这个词?BaseAdapter没有getFilter()方法。 - User
4
不需要好的捕捉。我的自定义适配器类(实际上是从AmazingListView开源小部件继承而来的AmazingAdapter)实现了android.widget.Filterable接口,该接口需要getFilter()方法。 - DustinB
26
这个教程对我非常有帮助。不过有些地方讲得不太清楚,或者可能是我没仔细看。 - Sufian
1
@Dustin,getFilteredResults(constraint)方法是什么?我尝试了你的答案,但那个方法不支持ne。 - anuruddhika
1
@anuruddhika,这只是你进行任何过滤并返回结果的方法。这不是你正在覆盖的Android方法。 - DustinB
显示剩余4条评论

17

这里有一个有趣的例子

public Filter getFilter() {
    return new Filter() {

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            final FilterResults oReturn = new FilterResults();
            final ArrayList<station> results = new ArrayList<station>();
            if (orig == null)
                orig = items;
            if (constraint != null) {
                if (orig != null && orig.size() > 0) {
                    for (final station g : orig) {
                        if (g.getName().toLowerCase()
                                .contains(constraint.toString()))
                            results.add(g);
                    }
                }
                oReturn.values = results;
            }
            return oReturn;
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint,
                FilterResults results) {
            items = (ArrayList<station>) results.values;
            notifyDataSetChanged();
        }
    };
}

public void notifyDataSetChanged() {
    super.notifyDataSetChanged();
    notifyChanged = true;
}

2
在使用ORIG变量保存初始实例方面,您采用了出色的方法。它运行良好,谢谢。 - radhoo

2
在你的基类上添加toString重写方法。例如:
@Override
public String toString() {
    return this.name;
}

以上将您的列表转换为字符串列表。因此,您可以使用以下代码:
your_edit_text.addTextChangedListener(new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
        YourActivity.this.YourAdapter.getFilter().filter(arg0);
    }

    @Override
    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
    }

    @Override
    public void afterTextChanged(Editable arg0) {    
    }
});

这是一个快速答案,适用于那些不想覆盖适配器的getFilter函数的人。 - zekromWex

2
对于那些不需要使用Filterable接口的人,有一种更简单的解决方案。这种方法还可以正确处理notifyDataSetChanged(),而其他解决方案则无法做到。请注意,您需要在BaseAdapter中添加一个getArray()函数,该函数只返回传递给构造函数的数组对象。
public abstract class BaseFilterAdapter<T> extends BaseAdapter<T> {

    private List<T> original;
    private String lastFilter;

    public BaseFilterAdapter(Context context, List<T> array) {
        super(context, new LinkedList<T>());
        original = array;
        filter("");
    }

    protected abstract Boolean predicate(T element, String filter);

    public void filter(String filter) {
        lastFilter = filter;
        super.getArray().clear();
        for (T element : original)
            if (predicate(element, filter))
                super.getArray().add(element);
        super.notifyDataSetChanged();
    }

    @Override
    public List<T> getArray() {
        return original;
    }

    @Override
    public void notifyDataSetChanged() {
        filter(lastFilter);
    }
}

我的 BaseAdapter 类中没有需要覆盖的 getArray 方法。 - Alberto M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接