2010-03-25 15 views
13

Ho un ListView che è collegato a un ArrayAdapter in cui Artist è una mia classe semplice, che ha solo un id e un nome.Come scrivere un filtro personalizzato per ListView con ArrayAdapter

Ora voglio filtrata del ListView in modo che io chiamo:

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 prima dichiarazione di debug stampa un conteggio di 8. Questo è il numero di Corrent per listitems che iniziano con "bla", ma la spina con l ' esso. La seconda istruzione di debug stampa un conteggio di 1150 elementi. Questo è il numero completo di elementi nell'elenco.

Quindi in qualche modo il filtro non comunica all'adattatore che ha filtrato i dati sottostanti.

Voglio sapere ora: devo scrivere qualcosa nel mio adattatore in modo da ottenere gli aggiornamenti dal filtro? Devo scrivere un filtro personalizzato? Cosa devo fare?

+0

http://stackoverflow.com/questions/2718202/custom-filtering-in-android-using-arrayadapter - sembra che potrebbe rispondere alla tua domanda – stealthcopter

+0

@Anton: Hav u risolto it.Please risposta .. ....... –

risposta

1

Penso che sia possibile utilizzare notifyDataSetChanged(); nel metodo onFilterComplete.

26

realtà

ho notato che avrei dovuto essere utilizzando lista originalItems 'per costruire il nuovo filtrato uno in performFiltering.

Questo risolverà eventuali problemi che si vedono in merito alla modifica del testo nel filtro. Per esempio. cerchi "Pane", quindi torna indietro di una "B" e dovresti vedere tutte le "B". Nel mio post originale non avresti.

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

Fondamentalmente sto costruendo un'applicazione salute e nutrizione e uno schermo avrà un elenco di elementi in base all'indice glicemico/glicemico. Voglio che gli utenti siano in grado di digitare e avere il filtro automatico dello schermo. Ora, se usi solo le stringhe, ottieni il filtro automatico gratuitamente. Non sono però, ho la mia classe personalizzata GlycaemicIndexItem che ha proprietà su di esso. Devo fornire il mio filtraggio per garantire che l'elenco usato per essere disegnato sullo schermo sia aggiornato quando l'utente digita.

Attualmente lo schermo è un semplice ListActivity, con un ListView e un EditText (in cui l'utente digita). Alleghiamo un TextWatcher a questo EditText per assicurarci che ci vengano notificati gli aggiornamenti. Ciò significa che dovrebbe funzionare per tutti i dispositivi indipendentemente dal fatto che l'utente digiti su una tastiera rigida o morbida (ho un HTC DesireZ e un vecchio G1).

Ecco la xml layout per lo schermo/attività (Qualcuno può dirmi come incollare il codice XML in qui, come quando cerco di usare blocco di codice XML non ottiene incollato/visualizzate correttamente, ma interpretato):

layout for the activity - giatoz.xml

Come vogliamo visualizzare le nostre righe in stile personalizzato, abbiamo anche un file XML di layout per la riga stessa: Row xml file

ecco il codice per l'intera attività stessa. Estendendosi da ListActivity, questa classe ha una classe interna che funge da adattatore, che si estende da ArrayAdapter. Questo è istanziato nel onCreate dell'attività e ha passato un semplice elenco di stringhe per ora. Prestare attenzione a come viene creato nelle righe 39-40. Il nostro layout speciale per la riga viene passato con l'elenco di elementi.

La chiave per popolare le righe personalizzate è nel metodo dell'adattatore getView.

La nostra classe adattatore ha anche una propria classe interna chiamata GlycaemicIndexItemFilter che esegue il lavoro quando un utente digita. Il nostro filtro è associato al nostro EditText sulle righe 43-44 mediante l'uso di TextWatcher e il suo metodo dopoTextChanged. La linea 47 è l'indizio su come otteniamo il filtraggio. Chiamiamo filtro, sul nostro oggetto filtro. Il nostro filtro viene creato quando chiamiamo getFilter la prima volta, riga 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

puoi dare una demo facile per dirti come usarlo? grazie – pengwang

+0

faccio una domanda: http://stackoverflow.com/questions/5404197/listview-filter-mistake puoi aiutarmi qual è il mio errore – pengwang

+0

Ciao ho appena controllato la tua altra pagina e sembra che alcune persone ti abbiano già aiutato . È necessario controllare la riga 33 nella classe TestFilter. È una NullPointerException, che si verifica quando si tenta di chiamare un metodo o accedere a una proprietà su un'istanza di una classe (che è chiamata un oggetto) che non è stata ancora istanziata (creata). – TilleyTech

Problemi correlati