in Android, JAVA

Filtrare una ListView in Android

Forse è l’operazione più comune con cui abbiamo a che fare quando dobbiamo gestire una ListView nelle nostre applicazioni Android. Farlo non è poi cosi complicato, anzi, lo stesso sistema operativo ci facilità il compito fornendo tutti gli strumenti necessari per poter realizzare tale funzionalità.

In questa guida vedremo come è possibile applicare un filtro su una lista. Per farlo ho pensato di non utilizzare il classico esempio, array di stringhe, ma di gestire una lista di oggetti complessi (niente di cosi complicato).

Supponiamo di voler caricare e filtrare un elenco di “Contatti”, ognuno dei quali è cosi composto:

class Contact {
  private String name;
  private String surname;
  private String value;
  // ... Getter & Setter ...

  public boolean containsText(String keyword) {
    if (keyword == null || keyword.isEmpty()) {
      return true;
    }
    if (name.toLowerCase().contains(keyword)) {
      return true;
    }
    if (surname.toLowerCase().contains(keyword)) {
      return true;
    }
    if (value.contains(keyword)) {
      return true;
    }
    return false;
  }
}

Nel codice riportato è presente un metodo, containsText, che ci tornerà utile nei passaggi successivi per l’applicazione dei filtri. Per prima cosa dobbiamo realizzare un ArrayAdapter che ci consenta di gestire i dati da presentare all’interno della lista:

class ContactListViewAdapter extends ArrayAdapter<Contact> {
  private List mOriginalData;
  private List mFilteredData;
  public ContactListViewAdapter(Context context, int resource, List list) {
    super(context, resource, list);
    mOriginalData = list;
    mFilteredData = list;
  }
  
  @Override
  public int getCount() {
    return mFilteredData.size();
  }
  
  @Override
  public Contact getItem(int position) {
    return mFilteredData.get(position);
  }

  @Override
  public long getItemId(int position) {
    return position;
  } 
  
  @NonNull
  @Override
  public View getView(int position, final View convertView, ViewGroup parent) {
    // ... Render ...
  }
}

Nell’esempio riportato è stato effettuato l’override di alcuni metodi poiché, come è possibile notare dallo stesso codice, l’adapter realizzato riporta due List di contatti, la prima è la lista originale dei dati, mOriginalData, la seconda rappresenta quella filtrata, appunto mFilteredData. L’adapter prenderà come riferimento sempre l’elenco filtrato per caricare i contatti all’interno della ListView.

Cosi com’è ora, la lista non consente l’effettiva applicazione del filtro, ci manca ancora un passaggio, cioè la realizzazione del Filter, l’oggetto che a regime ci consentirà di applicare un criterio di ricerca e aggiornare l’elenco dei risultati. Per farlo dobbiamo modificare la classe ContactListViewAdapter aggiungendo un ulteriore dettaglio:

class ContactListViewAdapter implements ArrayAdapter extends Filterable {
  private class ItemFilter extends Filter {
    @Override
    protected Filter.FilterResults performFiltering(CharSequence constraint) {
      String filterString = constraint != null ? constraint.toString() : "";
      FilterResults results = new FilterResults();
      final List list = originalData;
      int count = list.size();
      final ArrayList<Contact> outputList = new ArrayList<>(count);
      Contact c;
      for (int i = 0; i < count; i++) {
        c = list.get(i);
        if (c.containsText(filterString)) {
          outputList.add(filterableEBook);
        }
      }

      results.values = outputList;
      results.count = outputList.size();

      return results;
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
      filteredData = (ArrayList<EBook>)results.values;
      notifyDataSetChanged();
    }

 }   
 
 // Aggiungo un nuovo campo nell'adapter:
 private ItemFilter mItemFilter = new ItemFilter();

 // Eseguo l'override del metodo preposto per l'applicazione dei filtri:
 @Override
 public Filter getFilter() {
   return mItemFilter;
 }
 // Proprietà precedentemente mostrate ... 
}

In pratica ciò che abbiamo fatto ora è implementare l’interfaccia Filterable che, successivamente, ci consente di applicare un filtro sulla nostra lista con estrema facilità. Utilizzando il metodo containsText presente nell’oggetto Contact, verifichiamo il match tra chiave di ricerca e contatto stesso per capire se deve o meno essere mostrato all’interno dei risultati.

Di seguito un esempio di codice con cui poter testare il risultato:

List<Contact> data= new ArrayList<>();
contactList.add(new Contact() {{
  setName("Roberto");
  setSurname("Conte Rosito");
  setValue("roberto.conterosito@gmail.com");
}});
contactList.add(new Contact() {{
  setName("Mario");
  setSurname("Rossi");
  setValue("mario.rossi@gmail.com");
}});
contactList.add(new Contact() {{
  setName("Mark");
  setSurname("Zuck");
  setValue("mark@facebook.com");
}});
mListViewAdapter = new ContactListViewAdapter(getBaseContext(), R.layout.item, data);
mListView = (ListView)findViewById(R.id.list);
mListView.setAdapter(mListViewAdapter);
postDelayed(new Runnable() {
  @Override
  public void run() {
    // Dopio 10 secondi vedremo comparire i soli contatti 
    // che hanno indirizzo Gmail!
    mListView.getFilter().filter("@gmail.com");
  }
}, 10 * 1000);

L’esempio riportato carica la lista e, dopo un tempo minimo di attesa (per simulare un esempio), applica un filtro che mostra i soli contatti che rispettano il criterio di ricerca specificato.

Scrivi un commento

Commento