2010-03-25 10 views
13

J'ai un ListView qui est connecté à un ArrayAdapter où Artist est une simple classe de miens, qui n'a qu'un ID et un nom.Comment écrire un filtre personnalisé pour ListView avec ArrayAdapter

Maintenant, je veux faire un filtrage du ListView donc j'appelle:

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 
    } 
}); 

La première instruction de débogage imprime un compte de 8. C'est le nombre de Corrent pour listitems qui commencent par « bla », mais l'adaptateur ne soit pas il. La deuxième instruction de débogage imprime un nombre de 1150 éléments. C'est le nombre total d'éléments dans la liste.

Ainsi, le filtre ne dit pas à l'adaptateur qu'il a filtré les données sous-jacentes.

Je veux savoir maintenant: dois-je faire quelque chose de code dans mon adaptateur afin qu'il obtienne les mises à jour du filtre? Dois-je écrire un filtre personnalisé? Qu'est-ce que je dois faire?

+0

http://stackoverflow.com/questions/2718202/custom-filtering-in-android-using-arrayadapter - ressemble à cela pourrait répondre à votre question – stealthcopter

+0

@Anton: HAV u .. it.please réponse résolu ....... –

Répondre

1

Je pense que vous pouvez utiliser notifyDataSetChanged(); dans la méthode onFilterComplete.

26

En fait

je remarquai que je suis en utilisant la liste des originalItems de construire le nouveau filtre dans un performFiltering.

Ceci corrigera tous les problèmes que vous voyez concernant la modification du texte dans le filtre. Par exemple. vous recherchez 'Pain' puis retour arrière à juste un 'B' et vous devriez voir tous les 'B'. Dans mon post original, vous n'auriez pas.

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 
     } 
    } 
} 

Fondamentalement, je fais construire une application de santé et de nutrition et un écran aura une liste d'éléments en fonction de l'index glycémique/glycémique. Je veux que les utilisateurs puissent taper et avoir l'autofiltre d'écran. Maintenant, si vous n'utilisez que des chaînes, vous obtiendrez un autofiltrage gratuit. Je ne suis pas cependant, j'ai ma propre classe personnalisée GlycaemicIndexItem qui a des propriétés dessus. Je dois fournir mon propre filtrage pour m'assurer que la liste utilisée à l'écran est mise à jour lorsque l'utilisateur tape.

Actuellement l'écran est un ListActivity simple, avec un ListView et un EditText (que l'utilisateur tape). Nous allons attacher un TextWatcher à cet EditText pour nous assurer que nous sommes informés des mises à jour. Cela signifie qu'il devrait fonctionner pour tous les appareils indépendamment de l'utilisateur qui tape sur un clavier dur ou un clavier logiciel (j'ai un HTC DesireZ et un vieux G1).

Voici la mise en page XML pour l'écran/activité (Quelqu'un peut-il me dire comment coller le code XML en ici, comme lorsque je tente d'utiliser xml bloc de code ne soit pas collé/afficher correctement, mais interprété):

layout for the activity - giatoz.xml

Comme nous voulons afficher nos lignes dans un style personnalisé, nous avons également un fichier xml de mise en page pour la ligne elle-même: Row xml file

Voici le code pour toute l'activité elle-même. S'étendant de ListActivity, cette classe a une classe interne qui agit comme l'adaptateur, qui s'étend de ArrayAdapter. Ceci est instancié dans le onCreate de l'activité et passé une simple liste de chaînes pour le moment. Faites attention à la façon dont il est créé sur les lignes 39-40. Notre disposition spéciale pour la ligne est transmise avec la liste des éléments.

La clé permettant de remplir les lignes personnalisées se trouve dans la méthode de l'adaptateur getView.

Notre classe d'adaptateur a également sa propre classe interne appelée GlycaemicIndexItemFilter qui effectue le travail lorsqu'un utilisateur tape. Notre filtre est lié à notre EditText sur les lignes 43-44 par l'utilisation d'un TextWatcher et sa méthode afterTextChanged. La ligne 47 est l'indice quant à la façon dont nous réalisons le filtrage. Nous appelons le filtre, sur notre objet filtre. Notre filtre est créé lorsque nous appelons getFilter pour la première fois, ligne 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

pouvez-vous donner une démo facile à dire que comment l'utiliser? merci – pengwang

+0

je pose une question: http://stackoverflow.com/questions/5404197/listview-filter-mistake pouvez-vous m'aider quelles sont mes erreurs – pengwang

+0

Salut, je viens de vérifier votre autre page et il semble que certaines personnes vous ont déjà aidé . Vous devez vérifier la ligne 33 dans votre classe TestFilter. C'est une exception NullPointerException, qui se produit lorsque vous essayez d'appeler une méthode ou d'accéder à une propriété sur une instance d'une classe (appelée un objet) qui n'a pas encore été instanciée (créée). – TilleyTech

Questions connexes