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

Posted

技术标签:

【中文标题】自定义 ArrayAdapter 上的 getFilter() 不起作用【英文标题】:getFilter() on a custom ArrayAdapter not working 【发布时间】:2019-04-13 22:50:18 【问题描述】:

我使用 Custom ArrayAdapter 来存储用户信息,例如 sammy、robert、lizie 都是一个用户对象,我使用用户类型 ArrayList 来存储所有ArrayList 的用户对象。

因为它不是字符串或 int (The ArrayList),默认的 getFilter 不起作用,我已经完成了我的研究,但是 getFilter 方法有效,所以我可以修改自己。

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

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

这是我的自定义适配器

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;

    



这里的用户类没什么特别的

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 的变体,但它们都不适合我,并且有很好解释的是 BaseAdapter 而不是 ArrayAdapter

我试过这个question,我也试过这个question,但对我不起作用。

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

EDIT 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;
    



搜索无法在 customadapter 上运行,但我认为我做错了什么。

我在搜索栏中输入了一些内容,但没有发生过滤

如果您想查看搜索栏代码,它并没有什么特别之处,只是普通

@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;




【问题讨论】:

您可以使用ArrayAdapter 的内置Filter,只要您的模型类从其toString() 方法返回您想要用于比较的String。也就是说,只需在 User 类中添加 @Override public String toString() return name; 。您根本不需要专门的 Filter 类或 getFilter() 方法。 Mike M. 它使用简单直接的方法完美运行 请回答这个问题 【参考方案1】:

ArrayAdapter 的内置 Filter 使用模型类的 toString() 返回(即其类型参数)来执行其过滤比较。如果您能够覆盖UsertoString() 方法以返回您想要比较的内容,则不一定需要自定义Filter 实现(前提是其过滤算法适合您的情况)。在这种情况下:

@Override
public String toString() 
    return name;

为了明确该算法是什么,ArrayAdapter 的默认过滤如下:

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

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


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

在这种情况下,您应该直接将BaseAdapter 子类化,为原始和过滤的集合维护您自己的Lists。您可以使用ArrayAdapter's source 作为指导。

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

覆盖getView(),而不使用从调用super.getView()返回的View。 无论出于何种原因,手动设置 TextView 上的文本。 维护和使用您自己的收藏;即数组,或Lists,或者你有什么。

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


最后,我要提一下 ListView 在撰写本文时基本上已被弃用,尽管尚未正式发布。当前的建议是改用RecyclerView。但是,对于那些刚接触 Android 编程的人来说,ListView 仍然可以作为了解此类回收适配器View 的整体设计的第一步。 RecyclerView 一开始可能会有点不知所措。

【讨论】:

直截了当的解决方案,【参考方案2】:
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();
            
      
     ;

最后,重写此方法并返回过滤器实例。

@Override
     public Filter getFilter() 
        return myFilter;
    

更多参考请见https://gist.github.com/tobiasschuerg/3554252

【讨论】:

用户在这里代表什么?适配器采用的主要用户 ArrayList ?在构造函数中? 我已根据您的回答进行了编辑,但我认为我做错了,我将新编辑的类粘贴到新的编辑中 你能简单解释一下是什么问题吗? 我已在您回答后的编辑中添加了修改后的customadapter,但搜索仍然无效 在 publishResults() 中添加调试点并检查列表的值 ---users = (ArrayList) results.values--- 这个列表应该被过滤掉。如果正确,则在 notifyDatasetChange 之后检查 getview() 被调用了多少次

以上是关于自定义 ArrayAdapter 上的 getFilter() 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

ListView 中自定义 ArrayAdapter 的自定义过滤

android中自定义ArrayAdapter中的自定义getFilter

ListView:setItemChecked 仅适用于标准 ArrayAdapter - 在使用自定义 ArrayAdapter 时不起作用?

onItemClickListener 未在自定义 ArrayAdapter 上触发

为自定义 ArrayAdapter 实现 Fiterable 接口

连续按钮的自定义arrayadapter和onclicklistener