自定义ArrayAdapter上的getFilter()无法正常工作 [英] getFilter() on a custom ArrayAdapter not working

查看:80
本文介绍了自定义ArrayAdapter上的getFilter()无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用自定义ArrayAdapter 存储用户信息,例如sammy,robert,lizie都是一个User对象,并且我正在使用用户类型 ArrayList 存储所有用户对象 ArrayList .

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

我想基于 User class

name 属性实现搜索

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

这是我的CustomAdapter

  Class CustomArrayAdapter扩展了ArrayAdapter< User>.实现可过滤{CustomArrayAdapter(@NonNull上下文上下文,ArrayList< User>用户){超级(上下文,0,用户);}@NonNull@Overridepublic View getView(int position,@Nullable View convertView,@NonNull ViewGroup parent){用户innserUser = getItem(position);如果(convertView == null){convertView = LayoutInflater.from(getContext()).inflate(R.layout.row_layout,parent,false);}TextView用户名=(TextView)convertView.findViewById(R.id.userNameContact);TextView userNumber =(TextView)convertView.findViewById(R.id.userNumberContact);ImageView userImage =(ImageView)convertView.findViewById(R.id.userImageContact);尝试 {if(innserUser!= null){username.setText(innserUser.name);userNumber.setText(innserUser.number);userImage.setImageBitmap(innserUser.imageBitmap);}}捕获(异常e){e.printStackTrace();}返回convertView;}} 

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

  import android.graphics.Bitmap;公共类用户{字符串ID,名称,数字;位图imageBitmap;用户(字符串ID,字符串名称,字符串编号,位图imageBitmap){this.id = id;this.name =名称;this.number =数字;this.imageBitmap = imageBitmap;}} 

我从许多线程中捆绑了许多 getFilter 的变体,但它们都不适合我,而那些很好解释的是BaseAdapter而不是ArrayAdapter

我已经尝试过

,如果您想查看搜索栏代码,它与通常的没什么不同

  @Overridepublic boolean onCreateOptionsMenu(Menu menu){MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.search_box,菜单);MenuItem item = menu.findItem(R.id.app_bar_search);SearchView searchView =(SearchView)item.getActionView();searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener(){@Overridepublic boolean onQueryTextSubmit(String query){返回false;}@Overridepublic boolean onQueryTextChange(String newText){customArrayAdapter.getFilter().filter(newText);返回false;}});返回true;} 

解决方案

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

  @Override公共字符串toString(){返回名称;} 

要确切了解该算法是什么, ArrayAdapter 的默认过滤如下:

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

如果这是一个合适的过滤器,那么到目前为止,该解决方案是最简单的.


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

在这种情况下,您应该直接为 BaseAdapter 子类化,为原始和过滤后的集合维护自己的 List .您可以使用 ArrayAdapter 的来源作为指南.

实际上,<代码> ArrayAdapter 经常选择一个<代码>适配器当延长是错误的选择. ArrayAdapter 设计用于一个单一的,有点简化的目标:在每个列表项的单个 TextView 上设置一个平面的 String .在许多情况下,子类化 ArrayAdapter 而不是 BaseAdapter 是毫无意义的和/或多余的.例如:

  • 重写 getView(),而不使用对 super.getView()的调用返回的 View .
  • 出于任何原因,自己手动设置 TextView 上的文本.
  • 维护和使用自己的收藏集;即数组或 List ,或者您拥有什么.

在这些情况和某些其他情况下,最好从一开始就使用 BaseAdapter .将 ArrayAdapter 用于比具有基本功能的单个文本项更复杂的事情会很快变得麻烦且容易出错,并且通常比其应有的麻烦还多.


最后,我要提到的是,在撰写本文时,虽然目前还没有正式发布,但基本上已经弃用了 ListView .当前的建议是改用 RecyclerView .但是,对于那些刚接触Android编程的人来说, ListView 仍然可以作为了解此类回收适配器 View 总体设计的开始.首先, RecyclerView 可能有点让人不知所措.

I am using a Custom ArrayAdapter to store User information for example sammy, robert, lizie are each one User objects and i am using a User type ArrayList to store all the User objects to ArrayList.

And because it is not a string or int (The ArrayList) the default getFilter does not work, and i have done my research but it is really confusing how the getFilter method works so i can modify myself.

I want to implement the searching based on the name property form the User class

I know i have to implement the Filterable interface in my CustomAdapter class, but the getFilter is really unintuitive.

Here is my 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;

    }


}

and here is the user class nothing special here

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;

    }
}

I tied alot of variations of the getFilter from many threads but none of them work for me ,and the one's with good explanations are for BaseAdapter not for ArrayAdapter

I have tried this question and i have tried this question but does not work for me.

I am new to android development field, and this seems particularly unintuitive. Any suggestions would be really appreciated, thank you.

EDIT 1: After the answer of jitesh mohite, Thanks for the replay 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;
    }


}

the search is not working on the customadapter still i think i am doing something wrong.

here i am typing something in the search bar but no filtering happens

and if you want to see the searchbar code its nothing special just the usual

@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's built-in Filter uses the toString() return from the model class (i.e., its type parameter) to perform its filtering comparisons. You don't necessarily need a custom Filter implementation if you're able to override User's toString() method to return what you want to compare (provided its filtering algorithm is suitable to your situation). In this case:

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

To be clear on exactly what that algorithm is, ArrayAdapter's default filtering goes as follows:

The filter String is first converted to lowercase. Then, looping over the dataset, each value's toString() return is converted to lowercase, and checked to see if it startsWith() the filter String. If so, it's added to the result set. If not, a second check is performed, whereby the value's lowercase String is split on a space (" "), and each value from that is compared to the filter, again using startsWith(). Basically, it first checks if the whole thing starts with the filter text, and then checks each word, if necessary.

If that's a suitable filter, then this solution is by far the simplest.


If that does not meet your needs, and you do actually need a custom Filter implementation, then you should just not use ArrayAdapter to begin with. ArrayAdapter maintains internal, private Lists for the original and filtered collections – initially populated from the collection passed in the constructor call – and you do not have access to those. This is why the custom Filter attempt shown does not work, as the displayed item count and the item returned from getItem(position) are coming from that internal filter List, not the one built in the custom Filter.

In that case, you should directly subclass BaseAdapter instead, maintaining your own Lists for the original and filtered collections. You can use ArrayAdapter's source as a guide.

Indeed, ArrayAdapter is often the wrong choice when choosing an Adapter to extend. ArrayAdapter is designed for a singular, somewhat simplistic goal: setting a flat String on a single TextView in each list item. There are several cases in which subclassing ArrayAdapter instead of BaseAdapter is rather pointless and/or redundant. For example:

  • Overriding getView() and not using the View returned from a call to super.getView().
  • Manually setting the text on the TextView yourself, for whatever reason.
  • Maintaining and using your own collections; i.e., the arrays, or Lists, or what have you.

In these and certain other cases, it's arguably better to use BaseAdapter from the start. Using ArrayAdapter for anything much more complex than single text items with basic functionality can quickly become cumbersome and error-prone, and is often more trouble than it's worth.


Lastly, I would mention that ListView is basically deprecated, at this point, though not yet officially, at the time of this writing. Current recommendations are to use RecyclerView instead. However, for those brand new to Android programming, ListView can still be useful as a beginning step in understanding the overall design of this type of recycling adapter View. RecyclerView can be a little overwhelming to start with.

这篇关于自定义ArrayAdapter上的getFilter()无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆