AutoCompleteTextView定制ArrayAdapter&安培;过滤性能 [英] AutoCompleteTextView custom ArrayAdapter & Filter performance

查看:247
本文介绍了AutoCompleteTextView定制ArrayAdapter&安培;过滤性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用的ArrayList℃的定制ArrayAdapter的AutoCompleteTextView;产品方式>

我来,一个AutoCompleteTextView必须自定义ArrayAdapter实现的可筛选的结论,你必须使自己的过滤。

I've came to the conclusion that a custom ArrayAdapter of an AutoCompleteTextView must implements Filterable and you have to make your own Filtering.

从<一个href=\"http://stackoverflow.com/questions/8784249/android-autocompletetextview-with-custom-adapter-filtering-not-working\">this SO-问题和放大器;接受的答案和这个例子,我已经做了以下ArrayAdapter:

From this SO-question & accepted answer and this example, I have made the following ArrayAdapter:

public class AutoCompleteAdapter extends ArrayAdapter<Product> implements Filterable
{
    // Logcat tag
    private static final String TAG = "AutoCompleteAdapter";

    // The OrderedProductItem we need to get the Filtered ProductNames
    OrderedProductItem orderedProductItem;

    private Context context;
    private ArrayList<Product> productsShown, productsAll;

    // Default Constructor for an ArrayAdapter
    public AutoCompleteAdapter(Context c, int layoutId, ArrayList<Product> objects, OrderedProductItem opi){
        // Though we don't use the Layout-ResourceID , we still need it for the super
        super(c, layoutId, objects);

        L.Log(TAG, "AutoCompleteAdapter Constructor", LogType.VERBOSE);

        // ArrayAdapter's setNotifyOnChange is true by default,
        // but I set it nonetheless, just in case
        setNotifyOnChange(true);

        context = c;
        replaceList(objects, true);
        orderedProductItem = opi;
    }

    // Setup the ListItem's UI-elements
    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        return createTextViewAsItem(position);
    }
    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent){
        return createTextViewAsItem(position);
    }
    // To prevent repetition, we have this private method
    private TextView createTextViewAsItem(int position){
        TextView label = new TextView(context);
        String name = "";
        if(productsShown != null && productsShown.size() > 0 && position >= 0 && position < productsShown.size() - 1)
            name = productsShown.get(position).getName();
        label.setText(name);

        return label;
    }

    // Replace the List
    // When the boolean is set, we replace this ArrayAdapter's List entirely,
    // instead of just the filtering
    @SuppressWarnings("unchecked")
    public void replaceList(ArrayList<Product> p, boolean replaceInitialList){
        if(p != null && p.size() > 0){
            productsShown = p;
            if(replaceInitialList)
                productsAll = (ArrayList<Product>)productsShown.clone();
            notifyDataSetChanged();
        }
    }

    // Since we are using an AutoCompleteTextView, the Filtering has been reset and we need to apply this ourselves..
    Filter filter = new Filter(){
        @Override
        public String convertResultToString(Object resultValue){
            return ((Product)resultValue).getName();
        }

        @Override
        protected FilterResults performFiltering(CharSequence constraint){
            FilterResults filterResults = new FilterResults();
            if(productsAll != null){
                // If no constraint is given, return the whole list
                if(constraint == null){
                    filterResults.values = productsAll;
                    filterResults.count = productsAll.size();
                }
                else if(V.notNull(constraint.toString(), true)){
                    L.Log(TAG, "performFiltering: " + constraint.toString(), LogType.VERBOSE);

                    ArrayList<Product> suggestions = new ArrayList<Product>();

                    if(p.size() > 0)
                        for(Product p : productsAll)
                            if(p.getName().toLowerCase(Locale.ENGLISH).contains(constraint.toString().toLowerCase(Locale.ENGLISH)))
                                suggestions.add(p);

                    filterResults.values = suggestions;
                    filterResults.count = suggestions.size();
                }
            }
            return filterResults;
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            if(results != null && results.count > 0)
                replaceList((ArrayList<Product>)results.values, false);
        }
    };

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

一切完美的作品。然而,因为我有周边产品1250名单和这些都是循环每次用户改变他在AutoCompleteTextView投入,包括建立两个新的实例(FilterResults和ArrayList)的时候,我在想,如果有一个更好的解决方案这无需循环尽管每个用户输入改变一切。

Everything works perfectly. However, since I have a list of around 1250 Products and these are all looped every time the User changes his input in the AutoCompleteTextView, including the creation of two new instantiations (FilterResults and ArrayList), I was wondering if there is a better solution for this without having to loop though everything on every user input change.

如果没有我只是保持这一点。我因为含1250左右的物体AutoCompleteTextView,使用自定义ArrayAdapter(包括自定义过滤)只是想和一个自定义TextWatcher,它不是性能好。特别是因为这AutoCompleteTextView使用ListView的项目内。这意味着我对每一个项目AutoCompleteTextView(可能从〜5到50,平均为15左右)。

If there isn't I just keep this. I was just wondering since with an AutoCompleteTextView containing around 1250 objects, with a custom ArrayAdapter (including custom Filtering) and a custom TextWatcher, it isn't that good for the performance. Especially since this AutoCompleteTextView is used inside the item of a ListView. Which means I have an AutoCompleteTextView for every item (potentially ranging from ~ 5 to 50, with an average of around 15).

推荐答案

这是后期即将相当,但是我想我会在权衡您的问题......主要是因为发生与实施,而吓跑的事情。要回答你的问题直接,没有太多可以很容易地做,以避免全的ArrayList 循环过滤时。如果你需要的东西更快,你需要寻找到pre-处理您的资料到的东西与更快的搜索时间。 <一href=\"http://programmers.stackexchange.com/questions/86106/search-for-a-sub-string-in-a-given-array-of-strings\">AutoComplete算法?。

This is coming fairly late, however I thought I'd weigh in on your problem...mostly because the rather scaring things occurring with your implementation. To answer your immediate question, there's not much you can easily do to avoid the full ArrayList iteration when filtering. If you need something faster, you'll need to look into pre-processing your data into something with faster search times. AutoComplete Algorithm?.

我有经验定制的 ArrayAdapter 过滤逻辑的一般规则。不这样做。每当你碰到这种情况,正确的办法是推出自己的解决方案的适配器(使用 BaseAdapter )...或者找一个的第三方解决方案,让您过。这个问题的部分原因是内部的 ArrayAdapter 都有它自己的两个列表进行过滤和它自己的内部同步锁。你的 AutoCompleteAdapter 被暴露一吨存取器,所有这些的同步对象无法同步上。这意味着如果在筛选发生的适配器突变你的风险并发问题。

I have a general rule of thumb for customizing the ArrayAdapter filtering logic. Don't do it. Whenever you run into this situation, the correct solution is to roll your own adapter solution (Using BaseAdapter)...or find a 3rd party solution that allows you too. Part of the issue is that internally the ArrayAdapter has it's own two lists for filtering and it's own internal synchronized lock. Your AutoCompleteAdapter is exposing a ton of mutators, all of which synchronize on an object you can't sync on. That means you risk concurrency issues if the adapter is mutated while filtering is occurring.

因为它的立场与code时, ArrayAdapter 链接了你的 productsAll 列表。任何突变,访问,方法等将始终引用列表。起初我很惊讶您的解决方案的工作!然后,我没有使用的getItem 为是常态实现。我想你完全忽略了其他所有的 ArrayAdapter 方法,否则你会看到很奇怪的行为。如果是这样的话, ArrayAdapter 是不是真的为你做任何事情,你装载了白白这个巨大的类。将琐碎与 BaseAdapter 切换出来。

As it stands with your code, the ArrayAdapter is linked up with your productsAll list. Any mutations, accessors, methods etc will always reference that list. At first I was surprised your solution worked! Then I realized you aren't using getItem as is the norm. I imagine you are completely neglecting all the other ArrayAdapter methods, else you'd have seen rather strange behavior. If that's the case, ArrayAdapter isn't really doing anything for you and you're loading up this huge class for nothing. Would be trivial to switch it out with BaseAdapter.

其实我很惊讶你没有看到其他奇怪的问题。举例来说,不管你的过滤列表显示的东西,你的适配器总是注册 productsAll 列表计算,而不是 productsShown 计数。这可能是为什么你有这些索引越界检查?通常情况下没有必要的。

In fact I'm surprised you aren't seeing other strange problems. For instance, no matter what your filtered list shows, your adapter is always registering the productsAll list count instead of the productsShown count. Which may be why you have all these index out of bounds checks? Typically not needed.

我也吃了一惊,因为你无法调用你的过滤操作更新列表 notifyDataSetChanged 完成时。

I'm also surprised your filtering operation updates the list since you fail to invoke notifyDataSetChanged when finished.

接下来大问题,你永远不应该巢适配器。我通常主张,是因为人们嵌入列表视图 ...这是另一种不无本身。这是我第一次听说过用 AutoCompleteTextView 筑巢虽然。略有不同的情况,但我还是会说这是一个坏主意。为什么?谁也不能保证有多少次 getView 将调用一个给定的位置。这可以称之为一次...它可以把它的4倍......以上。所以,想象重新创建适配器每个项目的4倍。即使只有10个项目同时显示,你看你的自定义适配器的40实例!我当然希望你想出一个办法来回收这些适配器来降低这一数字。

Next big problem, you should never nest adapters. I'm usually advocating this because people embed ListViews...which is another no no of itself. This is the first I've heard about nesting with AutoCompleteTextView though. Slightly different situation, yet I'd still say this is a bad idea. Why? There's no guarantee how many times getView will be invoked for a given position. It could call it once...it could call it 4 times...or more. So imagine recreating your adapter 4 times per item. Even if only 10 items display at a time, you're looking at 40 instantiations of your custom adapter! I sure hope you figured out a way to recycle those adapters to lower that number.

不过考虑到你不使用 ViewHolder 我假设你甚至不知道的回收行为? ViewHolder 是一个必须为任何适配器做。它轻而易举地单将提供巨大的性能夸。现在,你创建的每 getView 调用一个新的观点和忽略任何提供的回收意见。有一百万在线的例子是展示和解释 ViewHolder 。这里有一个这样的链接

However considering you aren't using the ViewHolder I'm assuming you don't even know about the recycling behavior? ViewHolder is a must do for any adapter. It single handily will provide an enormous performance boast. Right now, you are creating a new view with every getView invocation and ignoring any of the recycled views provided. There a million examples online that show and explain the ViewHolder. Here's one such link.

侧面说明, ArrayAdapter 已经实现了可筛选。重新加入实现了您的自定义适配器是不必要的。

Side note, ArrayAdapter already implements Filterable. Re-adding the implements in your custom adapter is not needed.

要总结一下:


  • 实施 BaseAdapter 而不是

  • 请不要嵌入适配器。找到一个不同的方式,而不需要多次,以显示你的UI AutoCompleteTextViews A 的ListView

  • 无法真正提高你的过滤逻辑,而一些重磅数据pre-处理。

  • 使用ViewHolder范例。

  • Implement BaseAdapter instead
  • Don't embed adapters. Find a different way to display your UI without requiring multiple AutoCompleteTextViews inside of a ListView.
  • Can't really improve your filtering logic without some heavy data pre-processing.
  • Use ViewHolder paradigm.

一定要从谷歌我看视频了解列表视图和适配器/ O。
下面是一些进一步读数的有关 ArrayAdapter

Must watch video from Google I/O about ListViews and adapters. Here's some further readings about the ArrayAdapter.

这篇关于AutoCompleteTextView定制ArrayAdapter&安培;过滤性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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