2010-04-27 13 views
38

Próbuję filtrować moje ListView, który jest wypełniana z tym ArrayAdapter:Niestandardowe filtrowanie w Androidzie używając ArrayAdapter

package me.alxandr.android.mymir.adapters; 

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.Collections; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.Set; 

import me.alxandr.android.mymir.R; 
import me.alxandr.android.mymir.model.Manga; 
import android.content.Context; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.ArrayAdapter; 
import android.widget.Filter; 
import android.widget.SectionIndexer; 
import android.widget.TextView; 

public class MangaListAdapter extends ArrayAdapter<Manga> implements SectionIndexer 
{ 
    public ArrayList<Manga> items; 
    public ArrayList<Manga> filtered; 
    private Context context; 
    private HashMap<String, Integer> alphaIndexer; 
    private String[] sections = new String[0]; 
    private Filter filter; 
    private boolean enableSections; 

    public MangaListAdapter(Context context, int textViewResourceId, ArrayList<Manga> items, boolean enableSections) 
    { 
     super(context, textViewResourceId, items); 
     this.filtered = items; 
     this.items = filtered; 
     this.context = context; 
     this.filter = new MangaNameFilter(); 
     this.enableSections = enableSections; 

     if(enableSections) 
     { 
      alphaIndexer = new HashMap<String, Integer>(); 
      for(int i = items.size() - 1; i >= 0; i--) 
      { 
       Manga element = items.get(i); 
       String firstChar = element.getName().substring(0, 1).toUpperCase(); 
       if(firstChar.charAt(0) > 'Z' || firstChar.charAt(0) < 'A') 
        firstChar = "@"; 

       alphaIndexer.put(firstChar, i); 
      } 

      Set<String> keys = alphaIndexer.keySet(); 
      Iterator<String> it = keys.iterator(); 
      ArrayList<String> keyList = new ArrayList<String>(); 
      while(it.hasNext()) 
       keyList.add(it.next()); 

      Collections.sort(keyList); 
      sections = new String[keyList.size()]; 
      keyList.toArray(sections); 
     } 
    } 

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

     Manga o = items.get(position); 
     if(o != null) 
     { 
      TextView tt = (TextView) v.findViewById(R.id.MangaRow_MangaName); 
      TextView bt = (TextView) v.findViewById(R.id.MangaRow_MangaExtra); 
      if(tt != null) 
       tt.setText(o.getName()); 
      if(bt != null) 
       bt.setText(o.getLastUpdated() + " - " + o.getLatestChapter()); 

      if(enableSections && getSectionForPosition(position) != getSectionForPosition(position + 1)) 
      { 
       TextView h = (TextView) v.findViewById(R.id.MangaRow_Header); 
       h.setText(sections[getSectionForPosition(position)]); 
       h.setVisibility(View.VISIBLE); 
      } 
      else 
      { 
       TextView h = (TextView) v.findViewById(R.id.MangaRow_Header); 
       h.setVisibility(View.GONE); 
      } 
     } 

     return v; 
    } 

    @Override 
    public void notifyDataSetInvalidated() 
    { 
     if(enableSections) 
     { 
      for (int i = items.size() - 1; i >= 0; i--) 
      { 
       Manga element = items.get(i); 
       String firstChar = element.getName().substring(0, 1).toUpperCase(); 
       if(firstChar.charAt(0) > 'Z' || firstChar.charAt(0) < 'A') 
        firstChar = "@"; 
       alphaIndexer.put(firstChar, i); 
      } 

      Set<String> keys = alphaIndexer.keySet(); 
      Iterator<String> it = keys.iterator(); 
      ArrayList<String> keyList = new ArrayList<String>(); 
      while (it.hasNext()) 
      { 
       keyList.add(it.next()); 
      } 

      Collections.sort(keyList); 
      sections = new String[keyList.size()]; 
      keyList.toArray(sections); 

      super.notifyDataSetInvalidated(); 
     } 
    } 

    public int getPositionForSection(int section) 
    { 
     if(!enableSections) return 0; 
     String letter = sections[section]; 

     return alphaIndexer.get(letter); 
    } 

    public int getSectionForPosition(int position) 
    { 
     if(!enableSections) return 0; 
     int prevIndex = 0; 
     for(int i = 0; i < sections.length; i++) 
     { 
      if(getPositionForSection(i) > position && prevIndex <= position) 
      { 
       prevIndex = i; 
       break; 
      } 
      prevIndex = i; 
     } 
     return prevIndex; 
    } 

    public Object[] getSections() 
    { 
     return sections; 
    } 

    @Override 
    public Filter getFilter() 
    { 
     if(filter == null) 
      filter = new MangaNameFilter(); 
     return filter; 
    } 

    private class MangaNameFilter extends Filter 
    { 

     @Override 
     protected FilterResults performFiltering(CharSequence constraint) { 
      // NOTE: this function is *always* called from a background thread, and 
      // not the UI thread. 
      constraint = constraint.toString().toLowerCase(); 
      FilterResults result = new FilterResults(); 
      if(constraint != null && constraint.toString().length() > 0) 
      { 
       ArrayList<Manga> filt = new ArrayList<Manga>(); 
       ArrayList<Manga> lItems = new ArrayList<Manga>(); 
       synchronized (items) 
       { 
        Collections.copy(lItems, items); 
       } 
       for(int i = 0, l = lItems.size(); i < l; i++) 
       { 
        Manga m = lItems.get(i); 
        if(m.getName().toLowerCase().contains(constraint)) 
         filt.add(m); 
       } 
       result.count = filt.size(); 
       result.values = filt; 
      } 
      else 
      { 
       synchronized(items) 
       { 
        result.values = items; 
        result.count = items.size(); 
       } 
      } 
      return result; 
     } 

     @SuppressWarnings("unchecked") 
     @Override 
     protected void publishResults(CharSequence constraint, FilterResults results) { 
      // NOTE: this function is *always* called from the UI thread. 
      filtered = (ArrayList<Manga>)results.values; 
      notifyDataSetChanged(); 
     } 

    } 
} 

Jednak kiedy zadzwonić filtr („test”) na nic się dzieje w ogóle filtra (lub wątek w tle jest uruchamiany, ale lista nie jest filtrowana aż do użytkownika conservns). Jak mogę to naprawić?

+0

Od wpadłem na tej samej sytuacji po prostu pisał mój roztwór, który został zainspirowany tym thead jako GIST na github. Naprawiono także drobne problemy i ustawiono ogólny filtr. Zobacz https://gist.github.com/3554252 –

Odpowiedz

43

To naprawiło mój problem. Nie jestem pewien, czy to najlepsze rozwiązanie, ale działa. Mój projekt jest open-source, więc możesz użyć dowolnego kodu tutaj, jeśli okaże się przydatny :-).

package me.alxandr.android.mymir.adapters; 

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.Collections; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.Set; 

import me.alxandr.android.mymir.R; 
import me.alxandr.android.mymir.model.Manga; 
import android.content.Context; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.ArrayAdapter; 
import android.widget.Filter; 
import android.widget.SectionIndexer; 
import android.widget.TextView; 

public class MangaListAdapter extends ArrayAdapter<Manga> implements SectionIndexer 
{ 
    public ArrayList<Manga> items; 
    public ArrayList<Manga> filtered; 
    private Context context; 
    private HashMap<String, Integer> alphaIndexer; 
    private String[] sections = new String[0]; 
    private Filter filter; 
    private boolean enableSections; 

    public Manga getByPosition(int position) 
    { 
     return items.get(position); 
    } 

    public MangaListAdapter(Context context, int textViewResourceId, ArrayList<Manga> items, boolean enableSections) 
    { 
     super(context, textViewResourceId, items); 
     this.filtered = items; 
     this.items = ArrayList<Manga> items.clone(); 
     this.context = context; 
     this.filter = new MangaNameFilter(); 
     this.enableSections = enableSections; 

     if(enableSections) 
     { 
      alphaIndexer = new HashMap<String, Integer>(); 
      for(int i = items.size() - 1; i >= 0; i--) 
      { 
       Manga element = items.get(i); 
       String firstChar = element.getName().substring(0, 1).toUpperCase(); 
       if(firstChar.charAt(0) > 'Z' || firstChar.charAt(0) < 'A') 
        firstChar = "@"; 

       alphaIndexer.put(firstChar, i); 
      } 

      Set<String> keys = alphaIndexer.keySet(); 
      Iterator<String> it = keys.iterator(); 
      ArrayList<String> keyList = new ArrayList<String>(); 
      while(it.hasNext()) 
       keyList.add(it.next()); 

      Collections.sort(keyList); 
      sections = new String[keyList.size()]; 
      keyList.toArray(sections); 
     } 
    } 

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

     Manga o = filtered.get(position); 
     if(o != null) 
     { 
      TextView tt = (TextView) v.findViewById(R.id.MangaRow_MangaName); 
      TextView bt = (TextView) v.findViewById(R.id.MangaRow_MangaExtra); 
      if(tt != null) 
       tt.setText(o.getName()); 
      if(bt != null) 
       bt.setText(o.getLastUpdated() + " - " + o.getLatestChapter()); 

      if(enableSections && getSectionForPosition(position) != getSectionForPosition(position + 1)) 
      { 
       TextView h = (TextView) v.findViewById(R.id.MangaRow_Header); 
       h.setText(sections[getSectionForPosition(position)]); 
       h.setVisibility(View.VISIBLE); 
      } 
      else 
      { 
       TextView h = (TextView) v.findViewById(R.id.MangaRow_Header); 
       h.setVisibility(View.GONE); 
      } 
     } 

     return v; 
    } 

    @Override 
    public void notifyDataSetInvalidated() 
    { 
     if(enableSections) 
     { 
      for (int i = items.size() - 1; i >= 0; i--) 
      { 
       Manga element = items.get(i); 
       String firstChar = element.getName().substring(0, 1).toUpperCase(); 
       if(firstChar.charAt(0) > 'Z' || firstChar.charAt(0) < 'A') 
        firstChar = "@"; 
       alphaIndexer.put(firstChar, i); 
      } 

      Set<String> keys = alphaIndexer.keySet(); 
      Iterator<String> it = keys.iterator(); 
      ArrayList<String> keyList = new ArrayList<String>(); 
      while (it.hasNext()) 
      { 
       keyList.add(it.next()); 
      } 

      Collections.sort(keyList); 
      sections = new String[keyList.size()]; 
      keyList.toArray(sections); 

      super.notifyDataSetInvalidated(); 
     } 
    } 

    public int getPositionForSection(int section) 
    { 
     if(!enableSections) return 0; 
     String letter = sections[section]; 

     return alphaIndexer.get(letter); 
    } 

    public int getSectionForPosition(int position) 
    { 
     if(!enableSections) return 0; 
     int prevIndex = 0; 
     for(int i = 0; i < sections.length; i++) 
     { 
      if(getPositionForSection(i) > position && prevIndex <= position) 
      { 
       prevIndex = i; 
       break; 
      } 
      prevIndex = i; 
     } 
     return prevIndex; 
    } 

    public Object[] getSections() 
    { 
     return sections; 
    } 

    @Override 
    public Filter getFilter() 
    { 
     if(filter == null) 
      filter = new MangaNameFilter(); 
     return filter; 
    } 

    private class MangaNameFilter extends Filter 
    { 

     @Override 
     protected FilterResults performFiltering(CharSequence constraint) { 
      // NOTE: this function is *always* called from a background thread, and 
      // not the UI thread. 
      constraint = constraint.toString().toLowerCase(); 
      FilterResults result = new FilterResults(); 
      if(constraint != null && constraint.toString().length() > 0) 
      { 
       ArrayList<Manga> filt = new ArrayList<Manga>(); 
       ArrayList<Manga> lItems = new ArrayList<Manga>(); 
       synchronized (this) 
       { 
        lItems.addAll(items); 
       } 
       for(int i = 0, l = lItems.size(); i < l; i++) 
       { 
        Manga m = lItems.get(i); 
        if(m.getName().toLowerCase().contains(constraint)) 
         filt.add(m); 
       } 
       result.count = filt.size(); 
       result.values = filt; 
      } 
      else 
      { 
       synchronized(this) 
       { 
        result.values = items; 
        result.count = items.size(); 
       } 
      } 
      return result; 
     } 

     @SuppressWarnings("unchecked") 
     @Override 
     protected void publishResults(CharSequence constraint, FilterResults results) { 
      // NOTE: this function is *always* called from the UI thread. 
      filtered = (ArrayList<Manga>)results.values; 
      notifyDataSetChanged(); 
      clear(); 
      for(int i = 0, l = filtered.size(); i < l; i++) 
       add(filtered.get(i)); 
      notifyDataSetInvalidated(); 
     } 

    } 
} 
+0

Czy możesz napisać, jak używać tej klasy adaptera. Gdzie moja klasa POJO zawiera nazwę, telefon służbowy, telefon komórkowy, telefon służbowy i pocztę e-mail. –

+0

filtr działa doskonale, mój problem polega na tym, że po odfiltrowaniu listy i wyczyszczeniu mojego edittext.text wpisy w wykazie zniknęły. :( każda pomoc doceniona – cV2

+0

To powinno być dość proste do naprawienia.W moim kodzie, robię 'if (m.getName(). ToLowerCase(). Zawiera (ograniczenie))', aby sprawdzić, czy nazwa jest zgodna. Po prostu musisz sprawdzić, czy ograniczenie jest puste, i czy jest to dodanie wszystkich elementów do listy filt-arraylist (tak, jak sądzę, nie przetestowałem tego). – Alxandr

32

Nie czułem potrzeby wykonania żadnego z powyższych. Przesłonić metodę toString() z POJO jak ...

public String toString() { 
    return this.getCallTitle() == null ? "" :  
    this.getCallTitle().toLowerCase(); 
} 

TextWatcher jest po prostu filtrowanie i łatwiejszy sposób.

adapter.getFilter().filter(chars.toString().toLowerCase()); 

Jeśli nie masz niestandardowego filtrowania, to rozwiązanie działa. Mam nadzieję, że to przyniesie korzyści innym.

+5

Nie trzeba wykonywać konwersji lowerCase, ArrayAdapter już to robi. – Till

+3

W większości przypadków jest to znacznie ** lepsze ** niż przyjęte rozwiązanie. –

+0

Moja sugestia jest wypróbowanie tego najpierw przed próbą napisania własnego filtru, ponieważ bardzo łatwo jest od razu dowiedzieć się, czy działa, czy nie. Odpowiedź Alexandra jest dobrym przykładem na to, jak napisać prawdziwy filtr, ale ten rozwiązuje problem pod ręką w najbardziej zwięzły sposób. – sudocoder

12

Napisałem tę klasę, która jest w zasadzie kopią ArrayAdapter z niewielkimi modyfikacjami podobnymi do rozwiązania Alxandr, ale bardziej eleganckimi.

Wszystko co zrobiłem, ciągnęło logikę filtrowania z zagnieżdżonej klasy ArrayAdapter.ArrayFilter do klasy ArrayAdapter, aby ją odsłonić (i umożliwić użytkownikowi jej zastąpienie).

package com.example; 

/* Copyright (C) 2006 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. You may obtain a copy of the License at 
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
* either express or implied. See the License for the specific language governing permissions and limitations under the 
* License. */ 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Collection; 
import java.util.Collections; 
import java.util.Comparator; 
import java.util.List; 

import android.content.Context; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.ArrayAdapter; 
import android.widget.BaseAdapter; 
import android.widget.Filter; 
import android.widget.Filterable; 
import android.widget.TextView; 

/** 
* Slightly adopted ArrayAdapter. Basically a simple copy of ArrayAdapter with an extracted method filterObject() to 
* allow easy change of the filtering-behaviour. 
* 
* A concrete BaseAdapter that is backed by an array of arbitrary objects. By default this class expects that the 
* provided resource id references a single TextView. If you want to use a more complex layout, use the constructors 
* that also takes a field id. That field id should reference a TextView in the larger layout resource. 
* 
* <p> 
* However the TextView is referenced, it will be filled with the toString() of each object in the array. You can add 
* lists or arrays of custom objects. Override the toString() method of your objects to determine what text will be 
* displayed for the item in the list. 
* 
* <p> 
* To use something other than TextViews for the array display, for instance, ImageViews, or to have some of data 
* besides toString() results fill the views, override {@link #getView(int, View, ViewGroup)} to return the type of view 
* you want. 
* 
* @param <T> The class, this adapter should hold 
*/ 
public class OGArrayAdapter<T> extends BaseAdapter implements Filterable { 
    /** 
    * Contains the list of objects that represent the data of this ArrayAdapter. The content of this list is referred 
    * to as "the array" in the documentation. 
    */ 
    private List<T> mObjects; 

    /** 
    * Lock used to modify the content of {@link #mObjects}. Any write operation performed on the array should be 
    * synchronized on this lock. This lock is also used by the filter (see {@link #getFilter()} to make a synchronized 
    * copy of the original array of data. 
    */ 
    private final Object mLock = new Object(); 

    /** 
    * The resource indicating what views to inflate to display the content of this array adapter. 
    */ 
    private int mResource; 

    /** 
    * The resource indicating what views to inflate to display the content of this array adapter in a drop down widget. 
    */ 
    private int mDropDownResource; 

    /** 
    * If the inflated resource is not a TextView, {@link #mFieldId} is used to find a TextView inside the inflated 
    * views hierarchy. This field must contain the identifier that matches the one defined in the resource file. 
    */ 
    private int mFieldId = 0; 

    /** 
    * Indicates whether or not {@link #notifyDataSetChanged()} must be called whenever {@link #mObjects} is modified. 
    */ 
    private boolean mNotifyOnChange = true; 

    private Context mContext; 

    // A copy of the original mObjects array, initialized from and then used instead as soon as 
    // the mFilter ArrayFilter is used. mObjects will then only contain the filtered values. 
    private ArrayList<T> mOriginalValues; 
    private OGArrayFilter mFilter; 

    private LayoutInflater mInflater; 

    /** 
    * Constructor 
    * 
    * @param context The current context. 
    * @param textViewResourceId The resource ID for a layout file containing a TextView to use when instantiating 
    *   views. 
    */ 
    public OGArrayAdapter(Context context, int textViewResourceId) { 
     init(context, textViewResourceId, 0, new ArrayList<T>()); 
    } 

    /** 
    * Constructor 
    * 
    * @param context The current context. 
    * @param resource The resource ID for a layout file containing a layout to use when instantiating views. 
    * @param textViewResourceId The id of the TextView within the layout resource to be populated 
    */ 
    public OGArrayAdapter(Context context, int resource, int textViewResourceId) { 
     init(context, resource, textViewResourceId, new ArrayList<T>()); 
    } 

    /** 
    * Constructor 
    * 
    * @param context The current context. 
    * @param textViewResourceId The resource ID for a layout file containing a TextView to use when instantiating 
    *   views. 
    * @param objects The objects to represent in the ListView. 
    */ 
    public OGArrayAdapter(Context context, int textViewResourceId, T[] objects) { 
     init(context, textViewResourceId, 0, Arrays.asList(objects)); 
    } 

    /** 
    * Constructor 
    * 
    * @param context The current context. 
    * @param resource The resource ID for a layout file containing a layout to use when instantiating views. 
    * @param textViewResourceId The id of the TextView within the layout resource to be populated 
    * @param objects The objects to represent in the ListView. 
    */ 
    public OGArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects) { 
     init(context, resource, textViewResourceId, Arrays.asList(objects)); 
    } 

    /** 
    * Constructor 
    * 
    * @param context The current context. 
    * @param textViewResourceId The resource ID for a layout file containing a TextView to use when instantiating 
    *   views. 
    * @param objects The objects to represent in the ListView. 
    */ 
    public OGArrayAdapter(Context context, int textViewResourceId, List<T> objects) { 
     init(context, textViewResourceId, 0, objects); 
    } 

    /** 
    * Constructor 
    * 
    * @param context The current context. 
    * @param resource The resource ID for a layout file containing a layout to use when instantiating views. 
    * @param textViewResourceId The id of the TextView within the layout resource to be populated 
    * @param objects The objects to represent in the ListView. 
    */ 
    public OGArrayAdapter(Context context, int resource, int textViewResourceId, List<T> objects) { 
     init(context, resource, textViewResourceId, objects); 
    } 

    /** 
    * Adds the specified object at the end of the array. 
    * 
    * @param object The object to add at the end of the array. 
    */ 
    public void add(T object) { 
     synchronized (mLock) { 
      if (mOriginalValues != null) { 
       mOriginalValues.add(object); 
      } else { 
       mObjects.add(object); 
      } 
     } 
     if (mNotifyOnChange) 
      notifyDataSetChanged(); 
    } 

    /** 
    * Adds the specified Collection at the end of the array. 
    * 
    * @param collection The Collection to add at the end of the array. 
    */ 
    public void addAll(Collection<? extends T> collection) { 
     synchronized (mLock) { 
      if (mOriginalValues != null) { 
       mOriginalValues.addAll(collection); 
      } else { 
       mObjects.addAll(collection); 
      } 
     } 
     if (mNotifyOnChange) 
      notifyDataSetChanged(); 
    } 

    /** 
    * Adds the specified items at the end of the array. 
    * 
    * @param items The items to add at the end of the array. 
    */ 
    public void addAll(T... items) { 
     synchronized (mLock) { 
      if (mOriginalValues != null) { 
       Collections.addAll(mOriginalValues, items); 
      } else { 
       Collections.addAll(mObjects, items); 
      } 
     } 
     if (mNotifyOnChange) 
      notifyDataSetChanged(); 
    } 

    /** 
    * Inserts the specified object at the specified index in the array. 
    * 
    * @param object The object to insert into the array. 
    * @param index The index at which the object must be inserted. 
    */ 
    public void insert(T object, int index) { 
     synchronized (mLock) { 
      if (mOriginalValues != null) { 
       mOriginalValues.add(index, object); 
      } else { 
       mObjects.add(index, object); 
      } 
     } 
     if (mNotifyOnChange) 
      notifyDataSetChanged(); 
    } 

    /** 
    * Removes the specified object from the array. 
    * 
    * @param object The object to remove. 
    */ 
    public void remove(T object) { 
     synchronized (mLock) { 
      if (mOriginalValues != null) { 
       mOriginalValues.remove(object); 
      } else { 
       mObjects.remove(object); 
      } 
     } 
     if (mNotifyOnChange) 
      notifyDataSetChanged(); 
    } 

    /** 
    * Remove all elements from the list. 
    */ 
    public void clear() { 
     synchronized (mLock) { 
      if (mOriginalValues != null) { 
       mOriginalValues.clear(); 
      } else { 
       mObjects.clear(); 
      } 
     } 
     if (mNotifyOnChange) 
      notifyDataSetChanged(); 
    } 

    /** 
    * Sorts the content of this adapter using the specified comparator. 
    * 
    * @param comparator The comparator used to sort the objects contained in this adapter. 
    */ 
    public void sort(Comparator<? super T> comparator) { 
     synchronized (mLock) { 
      if (mOriginalValues != null) { 
       Collections.sort(mOriginalValues, comparator); 
      } else { 
       Collections.sort(mObjects, comparator); 
      } 
     } 
     if (mNotifyOnChange) 
      notifyDataSetChanged(); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public void notifyDataSetChanged() { 
     super.notifyDataSetChanged(); 
     mNotifyOnChange = true; 
    } 

    /** 
    * Control whether methods that change the list ({@link #add}, {@link #insert}, {@link #remove}, {@link #clear}) 
    * automatically call {@link #notifyDataSetChanged}. If set to false, caller must manually call 
    * notifyDataSetChanged() to have the changes reflected in the attached view. 
    * 
    * The default is true, and calling notifyDataSetChanged() resets the flag to true. 
    * 
    * @param notifyOnChange if true, modifications to the list will automatically call {@link #notifyDataSetChanged} 
    */ 
    public void setNotifyOnChange(boolean notifyOnChange) { 
     mNotifyOnChange = notifyOnChange; 
    } 

    private void init(Context context, int resource, int textViewResourceId, List<T> objects) { 
     mContext = context; 
     mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     mResource = mDropDownResource = resource; 
     mObjects = objects; 
     mFieldId = textViewResourceId; 
    } 

    /** 
    * Returns the context associated with this array adapter. The context is used to create views from the resource 
    * passed to the constructor. 
    * 
    * @return The Context associated with this adapter. 
    */ 
    public Context getContext() { 
     return mContext; 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    public int getCount() { 
     return mObjects.size(); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    public T getItem(int position) { 
     return mObjects.get(position); 
    } 

    /** 
    * Returns the position of the specified item in the array. 
    * 
    * @param item The item to retrieve the position of. 
    * 
    * @return The position of the specified item. 
    */ 
    public int getPosition(T item) { 
     return mObjects.indexOf(item); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    public long getItemId(int position) { 
     return position; 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    public View getView(int position, View convertView, ViewGroup parent) { 
     return createViewFromResource(position, convertView, parent, mResource); 
    } 

    private View createViewFromResource(int position, View convertView, ViewGroup parent, int resource) { 
     View view; 
     TextView text; 

     if (convertView == null) { 
      view = mInflater.inflate(resource, parent, false); 
     } else { 
      view = convertView; 
     } 

     try { 
      if (mFieldId == 0) { 
       // If no custom field is assigned, assume the whole resource is a TextView 
       text = (TextView) view; 
      } else { 
       // Otherwise, find the TextView field within the layout 
       text = (TextView) view.findViewById(mFieldId); 
      } 
     } catch (ClassCastException e) { 
      Log.e("ArrayAdapter", "You must supply a resource ID for a TextView"); 
      throw new IllegalStateException("ArrayAdapter requires the resource ID to be a TextView", e); 
     } 

     T item = getItem(position); 
     if (item instanceof CharSequence) { 
      text.setText((CharSequence) item); 
     } else { 
      text.setText(item.toString()); 
     } 

     return view; 
    } 

    /** 
    * <p> 
    * Sets the layout resource to create the drop down views. 
    * </p> 
    * 
    * @param resource the layout resource defining the drop down views 
    * @see #getDropDownView(int, android.view.View, android.view.ViewGroup) 
    */ 
    public void setDropDownViewResource(int resource) { 
     this.mDropDownResource = resource; 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public View getDropDownView(int position, View convertView, ViewGroup parent) { 
     return createViewFromResource(position, convertView, parent, mDropDownResource); 
    } 

    /** 
    * Creates a new ArrayAdapter from external resources. The content of the array is obtained through 
    * {@link android.content.res.Resources#getTextArray(int)}. 
    * 
    * @param context The application's environment. 
    * @param textArrayResId The identifier of the array to use as the data source. 
    * @param textViewResId The identifier of the layout used to create views. 
    * 
    * @return An ArrayAdapter<CharSequence>. 
    */ 
    public static ArrayAdapter<CharSequence> createFromResource(Context context, int textArrayResId, int textViewResId) { 
     CharSequence[] strings = context.getResources().getTextArray(textArrayResId); 
     return new ArrayAdapter<CharSequence>(context, textViewResId, strings); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    public Filter getFilter() { 
     if (mFilter == null) { 
      mFilter = new OGArrayFilter(); 
     } 
     return mFilter; 
    } 

    /** 
    * Performs filtering on the provided object and returns true, if the object should be in the filtered collection, 
    * or false if it shouldn't. 
    * 
    * @param myObject The object to be inspected 
    * @param constraint Constraint, that the object has to fulfil 
    * @return true, if the object should be in the filteredResult, false otherwise 
    */ 
    protected boolean filterObject(T myObject, String constraint) { 
     final String valueText = myObject.toString().toLowerCase(); 

     // First match against the whole, non-splitted value 
     if (valueText.startsWith(constraint)) { 
      return true; 
     } else { 
      final String[] words = valueText.split(" "); 
      final int wordCount = words.length; 

      // Start at index 0, in case valueText starts with space(s) 
      for (int k = 0; k < wordCount; k++) { 
       if (words[k].startsWith(constraint)) { 
        return true; 
       } 
      } 
     } 

     // No match, so don't add to collection 
     return false; 
    } 

    /** 
    * <p> 
    * An array filter constrains the content of the array adapter with a prefix. Each item that does not start with the 
    * supplied prefix is removed from the list. 
    * </p> 
    */ 
    private class OGArrayFilter extends Filter { 
     @Override 
     protected FilterResults performFiltering(CharSequence prefix) { 
      FilterResults results = new FilterResults(); 

      if (mOriginalValues == null) { 
       synchronized (mLock) { 
        mOriginalValues = new ArrayList<T>(mObjects); 
       } 
      } 

      if (prefix == null || prefix.length() == 0) { 
       ArrayList<T> list; 
       synchronized (mLock) { 
        list = new ArrayList<T>(mOriginalValues); 
       } 
       results.values = list; 
       results.count = list.size(); 
      } else { 
       String prefixString = prefix.toString().toLowerCase(); 

       ArrayList<T> values; 
       synchronized (mLock) { 
        values = new ArrayList<T>(mOriginalValues); 
       } 

       final int count = values.size(); 
       final ArrayList<T> newValues = new ArrayList<T>(); 

       for (int i = 0; i < count; i++) { 
        final T value = values.get(i); 
        if (filterObject(value, prefixString)) { 
         newValues.add(value); 
        } 
       } 

       results.values = newValues; 
       results.count = newValues.size(); 
      } 

      return results; 
     } 

     @Override 
     protected void publishResults(CharSequence constraint, FilterResults results) { 
      //noinspection unchecked 
      mObjects = (List<T>) results.values; 
      if (results.count > 0) { 
       notifyDataSetChanged(); 
      } else { 
       notifyDataSetInvalidated(); 
      } 
     } 
    } 
} 

Zaletą tego rozwiązania jest to piękno w aplikacji, po prostu poprzez nadpisanie filterObject-metody, która została wyciągnięta z ArrayAdapter.ArrayFilter klasy do ArrayAdapter:

public class MyAdapter extends OGArrayAdapter<OGScene> { 
    // Do the rest of your adapter 

    @Override 
    protected boolean filterObject(OGScene myObject, String constraint) { 
     // If true, the object will be in the list, if false, it will be filtered. 
     // Do your own filtering with myObject as you desire 
     return myObject.fulfillsConstraint(constraint); 
    } 
} 
+1

To naprawdę eleganckie rozwiązanie! –

+0

Dzięki za to! W moim przypadku musiałem zmienić 'list.get (position)' na 'getItem (position)', ale zadziałało to później. –

+0

Tylko notatkę. Aby korzystać z tego adaptera, nasz model Class (OGScene) musi przesłonić metodę toString() i ustawić właściwość, aby porównać ich. – Nepster

6

Najlepszym sposobem znalazłem się filtrować ArrayAdapter jest stworzyć własną klasę filtra:

private class MyFilter extends Filter 

następnie w tej funkcji utworzyć nową tablicę obiektów do wyświetlenia po filtr (można znaleźć dobrego wykonania w kodzie źródłowym class ArrayAdapter)

@Override 
protected FilterResults performFiltering(CharSequence prefix) 

teraz sztuką jest w tej metodzie

@Override 
protected void publishResults(CharSequence constraint, FilterResults results) 

podczas korzystania z zasilacza Array ty nie to zrobić:

myAdapterData = results.values 

odtąd odłączeniu dane z Super danych, należy to zrobić, aby zachować swoje odniesienie do super Oryginalny tablicę danych:

data.clear(); 
data.addAll((List<YourType>) results.values); 

i następnie zastąpić

getFilter()

w karty, na przykład:

@Override 
public Filter getFilter() { 
    if (filter == null) { 
     filter = new MyFilter(); 
    } 
    return filter; 
} 
+1

Dobry i prosty i działa, dzięki Ben :) –

+0

Wadą tego prostego rozwiązania jest to, że oryginalne dane listy są modyfikowane (tj. 'Data.clear(); data.add()'). więc w konstruktorze adaptera należy uzyskać klon oryginalnej listy (np. 'nowy MangaListAdapter (..., nowa ArrayList (myMangaData))') – k3b

Powiązane problemy