自定义 ArrayAdapter 上的 getFilter() 不起作用

getFilter() on a custom ArrayAdapter not working

我正在使用 Custom ArrayAdapter 来存储用户信息,例如 sammy、robert、lizie 都是用户对象,我正在使用用户类型 ArrayList 将所有用户对象存储到 ArrayList

并且因为它不是字符串或整数(ArrayList),所以默认的 getFilter 不起作用,我已经完成了研究,但确实令人困惑 getFilter 方法有效,所以我可以修改自己。

我想实现基于name属性形式的搜索User class

我知道我必须在我的 CustomAdapter class 中实现 Filterable 接口,但是 getFilter 真的很不直观。

这是我的 CustomAdapter

class CustomArrayAdapter extends ArrayAdapter<User>  implements Filterable {


    CustomArrayAdapter(@NonNull Context context, ArrayList<User> users) {
        super(context, 0, users);
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

        User innserUser = getItem(position);

        if (convertView == null){

            convertView = LayoutInflater.from(getContext()).inflate(R.layout.row_layout, parent, false);

        }

        TextView username = (TextView) convertView.findViewById(R.id.userNameContact);
        TextView userNumber = (TextView) convertView.findViewById(R.id.userNumberContact);
        ImageView userImage = (ImageView) convertView.findViewById(R.id.userImageContact);

        try {
            if(innserUser != null) {
                username.setText(innserUser.name);
                userNumber.setText(innserUser.number);
                userImage.setImageBitmap(innserUser.imageBitmap);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        return convertView;

    }


}

这里是用户 class 这里没什么特别的

import android.graphics.Bitmap;

public class User {

    String id, name, number;
    Bitmap imageBitmap;

    User(String id, String name, String number, Bitmap imageBitmap){

        this.id = id;
        this.name = name;
        this.number = number;
        this.imageBitmap = imageBitmap;

    }
}

我从许多线程中绑定了 getFilter 的很多变体,但其中 none 对我有用,并且有很好解释的是 BaseAdapter 而不是 ArrayAdapter

我试过这个 question and i have tried this question 但对我不起作用。

我是 android 开发领域的新手,这似乎特别不直观。 任何建议将不胜感激,谢谢。

编辑 1:在 jitesh mohite 回答后,感谢重播 jitesh mohite

class CustomArrayAdapter extends ArrayAdapter<User>  implements Filterable {


    ArrayList<User> users;

    CustomArrayAdapter(@NonNull Context context, ArrayList<User> users) {
        super(context, 0, users);
        this.users = users;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

        User innserUser = getItem(position);

        if (convertView == null){

            convertView = LayoutInflater.from(getContext()).inflate(R.layout.row_layout, parent, false);

        }

        TextView username = (TextView) convertView.findViewById(R.id.userNameContact);
        TextView userNumber = (TextView) convertView.findViewById(R.id.userNumberContact);
        ImageView userImage = (ImageView) convertView.findViewById(R.id.userImageContact);

        try {
            if(innserUser != null) {
                username.setText(innserUser.name);
                userNumber.setText(innserUser.number);
                userImage.setImageBitmap(innserUser.imageBitmap);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        return convertView;

    }


    Filter myFilter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults filterResults = new FilterResults();
            ArrayList<User> tempList=new ArrayList<User>();
            // Add the filter code here
            if(constraint != null && users != null) {
                int length= users.size();
                int i=0;
                while(i<length){
                    User item= users.get(i);
                    //do whatever you wanna do here
                    //adding result set output array

                    //item.name is user.name cause i want to search on name
                    if(item.name.toLowerCase().contains(constraint.toString().toLowerCase()) ) { // Add check here, and fill the tempList which shows as a result

                        tempList.add(item);
                    }

                    i++;
                }
                //following two lines is very important
                //as publish result can only take FilterResults users
                filterResults.values = tempList;
                filterResults.count = tempList.size();
            }
            return filterResults;
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence contraint, FilterResults results) {
            users = (ArrayList<User>) results.values;
            if (results.count > 0) {
                notifyDataSetChanged();
            } else {
                notifyDataSetInvalidated();
            }
        }
    };

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


}

自定义适配器上的搜索不工作我仍然认为我做错了什么。

我在搜索栏中输入内容,但没有过滤

如果您想查看搜索条码,它没什么特别的,只是通常的

@Override
public boolean onCreateOptionsMenu(Menu menu) {

    MenuInflater inflater = getMenuInflater();

    inflater.inflate(R.menu.search_box, menu);

    MenuItem item = menu.findItem(R.id.app_bar_search);

    SearchView searchView = (SearchView)item.getActionView();

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String newText) {

            customArrayAdapter.getFilter().filter(newText);

            return false;
        }
    });


    return true;



}
Filter myFilter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
         FilterResults filterResults = new FilterResults();   
         ArrayList<User> tempList=new ArrayList<User>();
         // Add the filter code here
         if(constraint != null && users!=null) {
             int length= users.size();
             int i=0;
                while(i<length){
                    User item= users.get(i);
                    //do whatever you wanna do here
                    //adding result set output array    
                    if()  {  // Add check here, and fill the tempList which shows as a result

                    tempList.add(item);
                  }

                    i++;
                }
                //following two lines is very important
                //as publish result can only take FilterResults users
                filterResults.values = tempList;
                filterResults.count = tempList.size();
          }
          return filterResults;
      }

      @SuppressWarnings("unchecked")
      @Override
      protected void publishResults(CharSequence contraint, FilterResults results) {
          users = (ArrayList<User>) results.values;
          if (results.count > 0) {
           notifyDataSetChanged();
          } else {
              notifyDataSetInvalidated();
          }  
      }
     };

最后,覆盖此方法和 return 过滤器实例。

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

有关更多参考,请参阅 https://gist.github.com/tobiasschuerg/3554252

ArrayAdapter 的 built-in Filter 使用模型 class 中的 toString() return(即它的类型参数)来执行其过滤比较。如果您能够将 UsertoString() 方法覆盖到 return 您想要比较的内容(提供其过滤算法),则不一定需要自定义 Filter 实现适合你的情况)。在这种情况下:

@Override
public String toString() {
    return name;
}

为了明确该算法的确切含义,ArrayAdapter 的默认过滤如下:

过滤器String首先转换为小写。然后,遍历数据集,将每个值的 toString() return 转换为小写,并检查它是否 startsWith() 过滤器 String。如果是,则将其添加到结果集中。如果不是,则执行第二次检查,其中值的小写 String 在 space (" ") 上拆分,并将来自该值的每个值与过滤器进行比较,再次使用 startsWith()。基本上,它首先检查整个内容是否以过滤文本开头,然后在必要时检查每个单词。

如果这是一个合适的过滤器,那么这个解决方案是迄今为止最简单的。


如果这不能满足您的需求,并且您确实需要自定义 Filter 实现,那么您不应该一开始就使用 ArrayAdapterArrayAdapter 维护内部,private Lists 用于原始和过滤后的 collections – 最初从构造函数调用中传递的 collection 填充 – 你无权访问这些。这就是显示的自定义 Filter 尝试不起作用的原因,因为显示的项目计数和从 getItem(position) 编辑的项目 return 来自内部过滤器 List,而不是一个内置于自定义 Filter.

在这种情况下,您应该直接子class BaseAdapter,维护您自己的 List 用于原始和过滤后的 collection。您可以使用 ArrayAdapter's source 作为指南。

确实,在选择 Adapter 进行扩展时,ArrayAdapter 通常是错误的选择。 ArrayAdapter 是为一个单一的、有些简单的目标而设计的:在每个列表项中的单个 TextView 上设置一个平面 String。在某些情况下,subclassing ArrayAdapter 而不是 BaseAdapter 是毫无意义的 and/or 多余的。例如:

  • 覆盖 getView() 并且不使用 View return 调用 super.getView().
  • 出于某种原因手动设置 TextView 上的文本。
  • 维护和使用您自己的 collection;即,数组,或 Lists,或者你有什么。

在这些和某些其他情况下,可以说从一开始就使用 BaseAdapter 更好。将 ArrayAdapter 用于比具有基本功能的单个文本项复杂得多的任何东西很快就会变得麻烦 error-prone,而且往往比它的价值更麻烦。


最后,我要提到的是 ListView 在撰写本文时基本上已被弃用,尽管尚未正式弃用。目前的建议是改用 RecyclerView。但是,对于 Android 编程的新手,ListView 仍然可以作为了解此类回收适配器 View 总体设计的第一步。 RecyclerView 一开始可能有点不知所措。