2010-03-25 7 views
13

Ich habe eine ListView, die mit einem ArrayAdapter verbunden ist, wo Künstler eine einfache Klasse von mir ist, die nur eine ID und einen Namen hat.Wie schreibe ich einen benutzerdefinierten Filter für ListView mit ArrayAdapter

Jetzt mag ich die Listview filtern, so nenne ich:

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

Die erste Debug-Anweisung druckt eine Anzahl von 8. Die für Listitem der corrent Zählung ist, die mit „bla“ starten, aber der Adapter nicht bekommen es. Die zweite Debug-Anweisung gibt eine Anzahl von 1150 Elementen aus. Das ist die vollständige Anzahl der Elemente in der Liste.

Also irgendwie sagt der Filter dem Adapter nicht, dass er die zugrunde liegenden Daten gefiltert hat.

Ich möchte jetzt wissen: habe ich etwas Code in meinem Adapter, damit es die Updates vom Filter bekommt? Muss ich einen benutzerdefinierten Filter schreiben? Was muss ich tun?

+0

http://stackoverflow.com/questions/2718202/custom-filtering-in-android-using-arrayadapter - sieht aus wie es Ihre Frage – stealthcopter

+0

@Anton beantworten könnte: Hav u it.Please Antwort gelöst .. ....... –

Antwort

1

Ich denke, Sie können notifyDataSetChanged(); in der Methode onFilterComplete verwenden.

26

Eigentlich

Ich bemerkte, dass ich 'originalItems Liste sein sollte mit dem neuen gefilterten ein in performFiltering zu bauen.

Dadurch werden alle Probleme behoben, die beim Ändern des Textes im Filter auftreten. Z.B. Du suchst nach 'Brot' und dann zurück zu nur 'B' und Du solltest alle 'B' sehen. In meinem ursprünglichen Beitrag hättest du nicht.

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

Grundsätzlich Ich baue eine Gesundheit und Ernährung Anwendung und ein Bildschirm wird eine Liste der Elemente basierend auf dem glykämischen/glykämischen Index hat. Ich möchte, dass Benutzer den Bildschirm Autofilter eingeben können und haben. Jetzt, wenn Sie nur Zeichenfolgen verwenden, erhalten Sie kostenlos Autofilter. Ich bin es nicht, ich habe meine eigene benutzerdefinierte Klasse GlycaemicIndexItem, die Eigenschaften darauf hat. Ich muss meine eigene Filterung bereitstellen, um sicherzustellen, dass die Liste, die auf dem Bildschirm gezeichnet wurde, aktualisiert wird, wenn der Benutzer tippt.

Derzeit ist der Bildschirm eine einfache ListActivity mit einem ListView und einem EditText (der vom Benutzer eingegeben wird). Wir werden an diesen EditText einen TextWatcher anhängen, um sicherzustellen, dass wir über Aktualisierungen informiert werden. Dies bedeutet, dass es für alle Geräte funktionieren sollte, unabhängig davon, ob der Benutzer auf einer harten oder einer weichen Tastatur tippt (ich habe ein HTC DesireZ und ein altes G1).

Hier ist das Layout XML für den Bildschirm/Aktivität (Kann mir jemand sagen, wie XML-Code in hier einzufügen, wie wenn ich versuche, Code-Block xml verwenden nicht ordnungsgemäß angezeigt eingefügt /, aber interpretiert):

layout for the activity - giatoz.xml

Wie wir wollen, dass unsere Reihen in benutzerdefinierten Stil anzuzeigen, haben wir auch eine Layout-xML-Datei für die Zeile selbst: Row xml file

Hier ist der Code für die gesamte Aktivität selbst. Ausgehend von ListActivity verfügt diese Klasse über eine innere Klasse, die als Adapter fungiert und sich von ArrayAdapter ausdehnt. Dies wird in onCreate der Aktivität instanziiert und eine einfache Liste von Zeichenfolgen für jetzt übergeben. Achten Sie darauf, wie es in den Zeilen 39-40 erstellt wird. Unser spezielles Layout für die Reihe wird mit der Artikelliste übergeben.

Der Schlüssel zum Auffüllen der benutzerdefinierten Zeilen ist in der Adaptermethode getView.

Unsere Adapterklasse hat auch eine eigene innere Klasse namens GlycaemicIndexItemFilter, die die Arbeit erledigt, wenn ein Benutzer eingibt. Unser Filter ist an unseren EditText in den Zeilen 43-44 unter Verwendung eines TextWatcher und seiner Methode afterTextChanged gebunden. Die Zeile 47 ist der Schlüssel zur Filterung. Wir rufen Filter auf unserem Filterobjekt auf. Unser Filter wird erstellt, wenn wir getFilter das erste Mal aufrufen, Zeile 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

Kannst du eine einfache Demo geben, um zu erfahren, wie man es benutzt? danke – pengwang

+0

ich stelle eine frage: http://stackoverflow.com/questions/5404197/listview-filter-mistake kannst du mir helfen was ist mein fehler – pengwang

+0

Hi ich habe gerade deine andere Seite überprüft und es sieht aus wie einige Leute dir schon geholfen haben . Sie müssen Zeile 33 in Ihrer TestFilter-Klasse überprüfen. Dies ist eine NullPointerException, die auftritt, wenn Sie versuchen, eine Methode aufzurufen oder auf eine Eigenschaft einer Instanz einer Klasse (die als Objekt bezeichnet wird) zuzugreifen, die noch nicht instanziiert (erstellt) wurde. – TilleyTech