【问题标题】:Filtering custom ListView with multiple TextViews using a Filter on an ArrayAdapter<T implements Parcelable>使用 ArrayAdapter<T implements Parcelable> 上的过滤器过滤具有多个 TextView 的自定义 ListView
【发布时间】:2017-05-03 18:07:50
【问题描述】:

我有一个类class CustomAdapter extends ArrayAdapter&lt;CustomListItem&gt;,其中CustomListItem implements Parcelable,并且有3个字符串变量(String a, b, c;

加载 ListView 时一切正常。但是,现在我想使用我的 SearchView 仅显示包含用户输入文本的列表元素。我希望来自 CustomAdapter 的过滤器查看该文本的 a、b 和 c,并显示包含该文本的任何列表项。

因此,例如,如果用户键入“ar”,并且 a b c 是"Rome", "Male", "Arnold",则无论哪个内容在哪个变量中,因为其中一个具有“Ar”(我不希望它是大小写敏感)我希望该项目显示在列表中。

目前,这个过滤器业务让我很困惑,stackoverflow 中的自定义过滤器似乎有很多答案,但我找不到具有我所描述的那种行为的答案。到目前为止,这就是我所拥有的:

public class CustomAdapter extends ArrayAdapter<CustomListItem> {

    public CustomAdapter(Context context, int textViewResourceId) {
        super(context, textViewResourceId);
    }

    public CustomAdapter(Context context, int resource, List<CustomListItem> items) {
        super(context, resource, items);
    }

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

        View v = convertView;

        if (v == null) {
            LayoutInflater vi;
            vi = LayoutInflater.from(getContext());
            v = vi.inflate(R.layout.row_layout, null);
        }

        CustomListItem s = getItem(position);

        if (s != null) {
            TextView a = (TextView) v.findViewById(R.id.a);
            TextView b = (TextView) v.findViewById(R.id.b);
            TextView c = (TextView) v.findViewById(R.id.c);

            if (a != null) {
                a.setText(s.getA());
            }

            if (b != null) {
                b.setText(s.getB());
            }

            if (c != null) {
                c.setText(s.getC());
            }
        }
        return v;
    }

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

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults result = new FilterResults();

                List<CustomListItem> list = new ArrayList<>();
                int max = getCount();
                for (int cont = 0; cont < max; cont++) {
                    if (constraint != null) {
                        CustomListItem item = getItem(cont);
                        boolean contains =
                                item.getA().toLowerCase().contains(constraint) ||
                                item.getB().toLowerCase().contains(constraint) ||
                                item.getC().toLowerCase().contains(constraint);
                        if (contains) {
                            list.add(getItem(cont));
                        }
                    } else {
                        list.add(getItem(cont));
                    }
                }

                result.values = list;
                result.count = list.size();

                return result;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                if (results.count > 0) {
                    notifyDataSetChanged();
                } else {
                    notifyDataSetInvalidated();
                }
            }
        };
    }
}

但这不起作用,可能是因为我不知道自己在做什么。这是自定义列表项:

public class CustomListItem implements Parcelable {
    private String a, b, c;

    public CustomListItem(String a, String b, String c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

    private CustomListItem(Parcel in) {
        a = in.readString();
        b = in.readString();
        c = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(a);
        dest.writeString(b);
        dest.writeString(c);
    }

    public String getA() {
        return a;
    }

    public String getB() {
        return b;
    }

    public String getC() {
        return c;
    }

    @Override
    public int describeContents(){
        return 0;
    }

    public static final Parcelable.Creator<CustomListItem> CREATOR
            = new Parcelable.Creator<CustomListItem>() {
        public CustomListItem createFromParcel(Parcel in) {
            return new CustomListItem(in);
        }

        public CustomListItem[] newArray(int size) {
            return new CustomListItem[size];
        }
    };
}

我在我的 AppCompatActivity 类中添加这样的过滤器:

SearchView searchView = (SearchView) findViewById(R.id.searchView);
final CustomAdapter adapter = new CustomAdapter(getApplicationContext(), R.layout.row_layout, item_list); //item_list is my list of custom items, defined elsewhere

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String text) {
        adapter.getFilter().filter(text);
        return true;
    }
    @Override
    public boolean onQueryTextChange(String text) {
        adapter.getFilter().filter(text);
        return true;
    }
});

我相信我的代码唯一有问题的是 getFilter() 方法,所以我正在寻找的答案是实现我刚才所说的正确而干净的方法。我也会很高兴解释我做错了什么,以及一些例子。感谢您的宝贵时间!

解决方案: 根据 Submersed 的回答,我进行了必要的更改以使代码正常工作。正如预期的那样,问题仅限于过滤器。但是,由于我更改了 CustomAdapter 类中的其他内容以实现我的解决方案,因此这是整个固定类:

public class CustomAdapter extends ArrayAdapter<CustomListItem> {

    private final List<CustomListItem> mList;

    public CustomAdapter(Context context, int resource, List<CustomListItem> items) {
        super(context, resource, items);
        mList = new ArrayList<>(items);
    }

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

        View v = convertView;

        if (v == null) {
            LayoutInflater vi;
            vi = LayoutInflater.from(getContext());
            v = vi.inflate(R.layout.row_layout, null);
        }

        CustomListItem s = getItem(position);

        if (s != null) {
            TextView a = (TextView) v.findViewById(R.id.a);
            TextView b = (TextView) v.findViewById(R.id.b);
            TextView c = (TextView) v.findViewById(R.id.c);

            if (a != null) {
                a.setText(s.getA());
            }

            if (b != null) {
                b.setText(s.getB());
            }

            if (c != null) {
                c.setText(s.getC());
            }
        }
        return v;
    }

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

            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                FilterResults result = new FilterResults();
                String constraint = charSequence.toString().toLowerCase();

                if (constraint == null || constraint.isEmpty()) {
                    result.values = mList;
                    result.count = mList.size();
                } else {
                    List<CustomListItem> list = new ArrayList<>();
                    int max = mList.size();
                    for (int cont = 0; cont < max; cont++) {
                        CustomListItem item = mList.get(cont);
                        boolean contains =
                                item.getA().toLowerCase().contains(constraint) ||
                                item.getB().toLowerCase().contains(constraint) ||
                                item.getC().toLowerCase().contains(constraint);
                        if (contains) {
                            list.add(mList.get(cont));
                        }
                    }
                    result.values = list;
                    result.count = list.size();
                }

                return result;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                clear();
                addAll((ArrayList<CustomListItem>) results.values);
                notifyDataSetChanged();
            }
        };
    }
}

【问题讨论】:

    标签: android listview android-arrayadapter parcelable android-filter


    【解决方案1】:

    在您的publishResults 方法中:

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                if (results.count > 0) {
                    notifyDataSetChanged();
                } else {
                    notifyDataSetInvalidated();
                }
            }
    

    您没有将适配器上的过滤结果设置为要在使数据集无效之前显示的新数据集。此外,您还应该保留原始值的副本,因此如果它们清空查询,您可以保留并重置原始结果。

    编辑框架示例代码: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/widget/ArrayAdapter.java#ArrayAdapter.ArrayFilter

    【讨论】:

    • 如何“将适配器上的过滤结果设置为要显示的新数据集”?
    • 您的过滤器已经引用了您的适配器,因此您只需调用 clear(),然后调用 addAll()。这就是为什么您需要保留原始值的副本。 ArrayAdapter 中已经有过滤器的默认实现,因此可能值得查看源代码。编辑链接,检查我们的 ArrayFilter。
    • 感谢您的帮助,根据您告诉我的内容,我现在已经做到了,并且会尽快更新我的问题并将您的答案标记为正确。最后一个问题:notifyDataSetChanged()notifyDataSetInvalidated() 方法有什么用?它们似乎与我无关。
    • notifyDataSetChanged 只是告诉您的适配器您已经更改了它显示的项目,因此它知道触发视图刷新。
    • 在我的问题中添加了固定代码。如果您可以查看它并告诉我您是否看到应该更改的内容,我们将不胜感激。否则,工作完成。谢谢!
    猜你喜欢
    • 2011-09-23
    • 2013-06-18
    • 2018-11-18
    • 2012-04-24
    • 2011-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-05
    相关资源
    最近更新 更多