2010-03-25 1 views
13

У меня есть ListView, который подключен к ArrayAdapter, где Artist - это мой простой класс, который имеет только идентификатор и имя.Как написать настраиваемый фильтр для ListView с ArrayAdapter

Теперь я хочу, чтобы фильтровать 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. Это граф corrent для ListItems, которые начинаются с «бла», но адаптер не получает Это. Второй отладочный оператор печатает счет 1150 элементов. Это полное количество элементов в списке.

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

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

+0

http://stackoverflow.com/questions/2718202/custom-filtering-in-android-using-arrayadapter - похоже, что это может ответить на ваш вопрос – stealthcopter

+0

@Anton: Hav U решил it.please ответ .. ....... –

ответ

1

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

26

На самом деле

я заметил, что я должен был с помощью списка «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 не получает вставили/отображается правильно, но интерпретированы):

layout for the activity - giatoz.xml

Как мы хотим показать наши строки в пользовательском стиле, у нас есть файл XML макет для самой строки: Row xml file

Вот код для самой всей деятельности. Расширяясь от 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 
      } 
     } 
    } 
} 
+0

Можете ли вы дать простое демо, чтобы рассказать, как его использовать? спасибо – pengwang

+0

Я задаю вопрос: http://stackoverflow.com/questions/5404197/listview-filter-mistake вы можете мне помочь, какие у меня ошибки – pengwang

+0

Привет, я просто проверил вашу другую страницу, и похоже, что некоторые люди уже помогли вам , Вам нужно проверить строку 33 в вашем классе TestFilter. Это исключение NullPointerException, которое происходит, когда вы пытаетесь вызвать метод или получить доступ к свойству экземпляра класса (который называется объектом), который еще не был создан (создан). – TilleyTech