14

我有一个 ListView,它连接到一个 ArrayAdapter,其中 Artist 是我的一个简单类,它只有一个 id 和一个名称。

现在我想过滤 ListView 所以我打电话:

artistAdapter.getFilter().filter("bla", new Filter.FilterListener() {
    public void onFilterComplete(int count) {
        Log.d(Config.LOG_TAG, "filter complete! count: " + count); // returns 8
        Log.d(Config.LOG_TAG, "adapter count: " + artistAdapter.getCount()); // return 1150
    }
});

第一个调试语句打印计数 8。这是以“bla”开头的列表项的正确计数,但适配器没有得到它。第二个调试语句打印计数 1150 项。这是列表中项目的完整数量。

所以不知何故,过滤器不会告诉适配器它已经过滤了底层数据。

我现在想知道:我是否在我的适配器中编写了一些代码,以便它从过滤器中获取更新?我必须编写自定义过滤器吗?我需要做什么?

4

2 回答 2

27

实际上

我注意到我应该在 performFiltering 中使用“originalItems”列表来构建新的过滤列表。

这将解决您看到的有关更改过滤器中的文本的任何问题。例如,您搜索“Bread”,然后退格到“B”,您应该会看到所有“B”。在我原来的帖子中,你不会有。

    private class GlycaemicIndexItemAdapter extends ArrayAdapter<GlycaemicIndexItem> {

    private ArrayList<GlycaemicIndexItem> items;
    private ArrayList<GlycaemicIndexItem> originalItems = new ArrayList<GlycaemicIndexItem>();
    private GlycaemicIndexItemFilter filter;
    private final Object mLock = new Object();

    public GlycaemicIndexItemAdapter(Context context, int textViewResourceId, ArrayList<GlycaemicIndexItem> newItems) {
            super(context, textViewResourceId, newItems);
            this.items = newItems;
            cloneItems(newItems);
    }

    protected void cloneItems(ArrayList<GlycaemicIndexItem> items) {
        for (Iterator iterator = items.iterator(); iterator
        .hasNext();) {
            GlycaemicIndexItem gi = (GlycaemicIndexItem) iterator.next();
            originalItems.add(gi);
        }
    }

    @Override
    public int getCount() {
        synchronized(mLock) {
            return items!=null ? items.size() : 0;  

    }

    @Override
    public GlycaemicIndexItem getItem(int item) {
        GlycaemicIndexItem gi = null;
        synchronized(mLock) {
                gi = items!=null ? items.get(item) : null;

        }
        return gi;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
            View v = convertView;
            if (v == null) {
                LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(R.layout.row, null);
            }

            GlycaemicIndexItem i  = null;
            synchronized(mLock) {
                i = items.get(position);
            }

            if (i != null) {
                    TextView tt = (TextView) v.findViewById(R.id.rowText);
                    TextView bt = (TextView) v.findViewById(R.id.rowText2);
                    if (tt != null) {
                          tt.setText("Name: "+i.getName());                            
                    }
                    if(bt != null){
                          bt.setText("GI Value: " + i.getGlycaemicIndex());
                    }
            }
            return v;
    }
    /**
     * Implementing the Filterable interface.
     */
    public Filter getFilter() {
        if (filter == null) {
            filter = new GlycaemicIndexItemFilter();
        }
        return filter;
    }   

    /**
     * Custom Filter implementation for the items adapter.
     *
     */
    private class GlycaemicIndexItemFilter extends Filter {
        protected FilterResults performFiltering(CharSequence prefix) {
            // Initiate our results object
            FilterResults results = new FilterResults();

            // No prefix is sent to filter by so we're going to send back the original array
            if (prefix == null || prefix.length() == 0) {
                synchronized (mLock) {
                    results.values = originalItems;
                    results.count = originalItems.size();
                }
            } else {
                synchronized(mLock) {
                        // Compare lower case strings
                    String prefixString = prefix.toString().toLowerCase();
                    final ArrayList<GlycaemicIndexItem> filteredItems = new ArrayList<GlycaemicIndexItem>();
                    // Local to here so we're not changing actual array
                    final ArrayList<GlycaemicIndexItem> localItems = new ArrayList<GlycaemicIndexItem>();
                    localItems.addAll(originalItems);
                    final int count = localItems.size();

                    for (int i = 0; i < count; i++) {
                        final GlycaemicIndexItem item = localItems.get(i);
                        final String itemName = item.getName().toString().toLowerCase();

                        // First match against the whole, non-splitted value
                        if (itemName.startsWith(prefixString)) {
                            filteredItems.add(item);
                        } else {} /* This is option and taken from the source of ArrayAdapter
                            final String[] words = itemName.split(" ");
                            final int wordCount = words.length;

                            for (int k = 0; k < wordCount; k++) {
                                if (words[k].startsWith(prefixString)) {
                                    newItems.add(item);
                                    break;
                                }
                            }
                        } */
                    }

                    // Set and return
                    results.values = filteredItems;
                    results.count = filteredItems.size();
                }//end synchronized
            }

            return results;
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence prefix, FilterResults results) {
            //noinspection unchecked
            synchronized(mLock) {
                final ArrayList<GlycaemicIndexItem> localItems = (ArrayList<GlycaemicIndexItem>) results.values;
                notifyDataSetChanged();
                clear();
                //Add the items back in
                for (Iterator iterator = localItems.iterator(); iterator
                        .hasNext();) {
                    GlycaemicIndexItem gi = (GlycaemicIndexItem) iterator.next();
                    add(gi);
                }
            }//end synchronized
        }
    }
}

基本上我正在构建一个健康和营养应用程序,一个屏幕将有一个基于血糖/血糖指数的项目列表。我希望用户能够键入并拥有屏幕自动过滤器。现在,如果您只使用字符串,您可以免费获得自动过滤。我不是,我有自己的自定义类 GlycaemicIndexItem,它有属性。我需要提供自己的过滤器,以确保在用户键入时更新用于在屏幕上绘制的列表。

当前屏幕是一个简单的 ListActivity,带有一个 ListView 和一个 EditText(用户输入)。我们将向此 EditText 附加一个 TextWatcher,以确保我们收到有关它的更新的通知。这意味着它应该适用于所有设备,无论用户在硬键盘还是软键盘上打字(我有一个 HTC DesireZ 和一个旧的 G1)。

这是屏幕/活动的布局 xml(有人可以告诉我如何将 xml 代码粘贴到此处,因为当我尝试使用代码块时,xml 没有正确粘贴/显示,而是被解释):

活动布局 - giatoz.xml

由于我们想以自定义样式显示我们的行,我们还有一个用于行本身的布局 xml 文件: 行xml文件

这是整个 Activity 本身的代码。从ListActivity扩展,这个类有一个作为适配器的内部类,它从ArrayAdapter扩展而来。这是在 Activity 的 onCreate 中实例化的,并暂时传递了一个简单的字符串列表。注意它是如何在第 39-40 行创建的。我们对行的特殊布局与项目列表一起传入。

填充自定义行的关键在于适配器的方法getView

我们的适配器类也有自己的内部类,称为 GlycaemicIndexItemFilter,它在用户键入时执行工作。我们的过滤器通过使用 TextWatcher 及其方法afterTextChanged绑定到第 43-44 行的 EditText 。第 47 行是我们如何实现过滤的线索。我们在过滤器对象上调用过滤器。我们的过滤器是在我们第一次调用 getFilter 时创建的,第 148-149 行。

   package com.tilleytech.android.myhealthylife;

     import java.util.ArrayList;
     import java.util.Iterator;

     import android.app.ListActivity;
      import android.content.Context;
     import android.content.res.Resources;
     import android.os.Bundle;
     import android.text.Editable;
      import android.text.TextWatcher;
      import android.view.LayoutInflater;
        import android.view.View;
       import android.view.ViewGroup;
       import android.widget.ArrayAdapter;
       import android.widget.EditText;
       import android.widget.Filter;
       import android.widget.ListView;
       import android.widget.TextView;


        public class GlycaemicIndexAtoZActivity extends ListActivity {
          /** Called when the activity is first created. */
        private GlycaemicIndexItemAdapter giAdapter; 
        private TextWatcher filterTextWatcher;
        private EditText filterText = null;

        @Override
        public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.giatoz);            

        ListView lv = getListView();
        lv.setTextFilterEnabled(true);
        // By using setAdapter method in listview we an add string array in list.
        ArrayList<GlycaemicIndexItem> list = getListItems();

        giAdapter = new GlycaemicIndexItemAdapter(this, R.layout.row, list);
        giAdapter.notifyDataSetChanged();
        setListAdapter(giAdapter);

        filterText = (EditText)findViewById(R.id.GI_AtoZSearchEditText);
        filterTextWatcher = new TextWatcher() {

            public void afterTextChanged(Editable s) {
                giAdapter.getFilter().filter(s); //Filter from my adapter
                giAdapter.notifyDataSetChanged(); //Update my view

            }

            public void beforeTextChanged(CharSequence s, int start, int count,
                    int after) {
            }

            public void onTextChanged(CharSequence s, int start, int before,
                    int count) {

            }

        };
        filterText.addTextChangedListener(filterTextWatcher);
    }

    private ArrayList<GlycaemicIndexItem> getListItems() {
        ArrayList<GlycaemicIndexItem> result = new ArrayList<GlycaemicIndexItem>();

        Resources res = getResources();
        //Get our raw strings
        String[] array = res.getStringArray(R.array.GIList);
        for (int i = 0; i < array.length; i++) {
            GlycaemicIndexItem gi = new GlycaemicIndexItem();
            gi.setName(array[i]);
            gi.setGlycaemicIndex(1);
            result.add(gi);
        }

        return result;
    }

    private class GlycaemicIndexItemAdapter extends ArrayAdapter<GlycaemicIndexItem> {

        private ArrayList<GlycaemicIndexItem> items;
        private ArrayList<GlycaemicIndexItem> originalItems = new ArrayList<GlycaemicIndexItem>();
        private GlycaemicIndexItemFilter filter;
        private final Object mLock = new Object();

        public GlycaemicIndexItemAdapter(Context context, int textViewResourceId, ArrayList<GlycaemicIndexItem> newItems) {
                super(context, textViewResourceId, newItems);
                this.items = newItems;
                cloneItems(newItems);
        }

        protected void cloneItems(ArrayList<GlycaemicIndexItem> items) {
            for (Iterator iterator = items.iterator(); iterator
            .hasNext();) {
                GlycaemicIndexItem gi = (GlycaemicIndexItem) iterator.next();
                originalItems.add(gi);
            }
        }

        @Override
        public int getCount() {
            synchronized(mLock) {
                return items!=null ? items.size() : 0;  
            }
        }

        @Override
        public GlycaemicIndexItem getItem(int item) {
            GlycaemicIndexItem gi = null;
            synchronized(mLock) {
                    gi = items!=null ? items.get(item) : null;

            }
            return gi;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
                View v = convertView;
                if (v == null) {
                    LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    v = vi.inflate(R.layout.row, null);
                }

                GlycaemicIndexItem i  = null;
                synchronized(mLock) {
                    i = items.get(position);
                }

                if (i != null) {
                        TextView tt = (TextView) v.findViewById(R.id.rowText);
                        TextView bt = (TextView) v.findViewById(R.id.rowText2);
                        if (tt != null) {
                              tt.setText("Name: "+i.getName());                            
                        }
                        if(bt != null){
                              bt.setText("GI Value: " + i.getGlycaemicIndex());
                        }
                }
                return v;
        }
        /**
         * Implementing the Filterable interface.
         */
        public Filter getFilter() {
            if (filter == null) {
                filter = new GlycaemicIndexItemFilter();
            }
            return filter;
        }   

        /**
         * Custom Filter implementation for the items adapter.
         *
         */
        private class GlycaemicIndexItemFilter extends Filter {
            protected FilterResults performFiltering(CharSequence prefix) {
                // Initiate our results object
                FilterResults results = new FilterResults();

                // No prefix is sent to filter by so we're going to send back the original array
                if (prefix == null || prefix.length() == 0) {
                    synchronized (mLock) {
                        results.values = originalItems;
                        results.count = originalItems.size();
                    }
                } else {
                    synchronized(mLock) {
                            // Compare lower case strings
                        String prefixString = prefix.toString().toLowerCase();
                        final ArrayList<GlycaemicIndexItem> filteredItems = new ArrayList<GlycaemicIndexItem>();
                        // Local to here so we're not changing actual array
                        final ArrayList<GlycaemicIndexItem> localItems = new ArrayList<GlycaemicIndexItem>();
                        localItems.addAll(originalItems);
                        final int count = localItems.size();

                        for (int i = 0; i < count; i++) {
                            final GlycaemicIndexItem item = localItems.get(i);
                            final String itemName = item.getName().toString().toLowerCase();

                            // First match against the whole, non-splitted value
                            if (itemName.startsWith(prefixString)) {
                                filteredItems.add(item);
                            } else {} /* This is option and taken from the source of ArrayAdapter
                                final String[] words = itemName.split(" ");
                                final int wordCount = words.length;

                                for (int k = 0; k < wordCount; k++) {
                                    if (words[k].startsWith(prefixString)) {
                                        newItems.add(item);
                                        break;
                                    }
                                }
                            } */
                        }

                        // Set and return
                        results.values = filteredItems;
                        results.count = filteredItems.size();
                    }//end synchronized
                }

                return results;
            }

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence prefix, FilterResults results) {
                //noinspection unchecked
                synchronized(mLock) {
                    final ArrayList<GlycaemicIndexItem> localItems = (ArrayList<GlycaemicIndexItem>) results.values;
                    notifyDataSetChanged();
                    clear();
                    //Add the items back in
                    for (Iterator iterator = localItems.iterator(); iterator
                            .hasNext();) {
                        GlycaemicIndexItem gi = (GlycaemicIndexItem) iterator.next();
                        add(gi);
                    }
                }//end synchronized
            }
        }
    }
}
于 2011-03-21T17:32:50.127 回答
1

我认为您可以notifyDataSetChanged();在 onFilterComplete 方法中使用。

于 2010-11-29T09:59:53.797 回答