Как написать настраиваемый фильтр для ListView с ArrayAdapter

У меня есть ListView, который связан с ArrayAdapter, где Artist – мой простой класс, который имеет только идентификатор и имя.

Теперь я хочу отфильтровать 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 элементов. Это полное количество элементов в списке.

Поэтому фильтр каким-то образом не сообщает адаптеру, что он отфильтровал базовые данные.

Я хочу знать сейчас: я что-то сделал в своем адаптере, чтобы получать обновления из фильтра? Нужно ли писать собственный фильтр? Что мне нужно сделать?

Solutions Collecting From Web of "Как написать настраиваемый фильтр для ListView с ArrayAdapter"

На самом деле

Я заметил, что я должен был использовать список «originalItems» для создания нового отфильтрованного в performFiltering.

Это устранит любые проблемы, которые вы видите относительно изменения текста в фильтре. Например, вы ищете «Хлеб», а затем назад к просто «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 (который пользователь вводит). Мы добавим TextWatcher в этот EditText, чтобы гарантировать, что мы будем уведомлены об обновлениях. Это означает, что он должен работать на всех устройствах независимо от ввода пользователем жесткой или мягкой клавиатуры (у меня есть HTC DesireZ и старый G1).

Вот макет xml для экрана / активности (может ли кто-нибудь сказать мне, как вставить здесь xml-код, поскольку, когда я пытаюсь использовать блок кода xml, вы не вставляете / отображаете правильно, а интерпретируете):

Макет для мероприятия - giatoz.xml

Поскольку мы хотим отображать наши строки в пользовательском стиле, у нас также есть XML-файл макета для самой строки: Строковый XML-файл

Вот код для всей самой Деятельности. Расширяясь от ListActivity, этот класс имеет внутренний класс, который действует как адаптер, который распространяется от ArrayAdapter. Это создается в onCreate Activity и теперь содержит простой список строк. Обратите внимание на то, как он создается на линиях 39-40. Наш специальный макет для строки передается вместе со списком элементов.

Ключом к заполнению пользовательских строк является метод getView адаптера .

Наш класс адаптеров также имеет свой собственный внутренний класс GlycaemicIndexItemFilter, который выполняет работу, когда пользователь вводит данные. Наш фильтр привязан к нашему EditText в строках 43-44 с помощью TextWatcher и его метода afterTextChanged . Линия 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 } } } } 

Я думаю, вы можете использовать notifyDataSetChanged(); В методе onFilterComplete.