2011-04-25 5 views
62

J'essaie d'implémenter le filtrage d'un ListView qui utilise un adaptateur d'objet personnalisé, mais je ne trouve aucun échantillon utile. Le code inclus est très simplifié, donc non- gardez à l'esprit que je ne peux pas utiliser un ArrayAdapter standard. J'ai un EditText au-dessus du ListView, et quand l'utilisateur entre le texte dans le widget EditText je voudrais filtrer le ListView par le texte écrit dans le EditText. Toutes les suggestions seraient grandement appréciées!Filtrage ListView avec adaptateur (objet) personnalisé

Voici l'extrait de la classe d'activité:

public class management_objects extends Activity { 

private static List<User> UserList; 
private EfficientAdapter adapter = null; 
private ListView objectListView = null; 
private EditText SearchText = null; 

private static class EfficientAdapter extends BaseAdapter implements Filterable{ 
    private LayoutInflater mInflater; 

    public EfficientAdapter(Context context) { 
     mInflater = LayoutInflater.from(context); 
    } 

    public int getCount() { 
     return UserList.size(); 
    } 

    public Object getItem(int position) { 
     return position; 
    } 

    public long getItemId(int position) { 
     return position; 
    } 

    public View getView(int position, View convertView, ViewGroup parent) { 
     ViewHolder holder; 
     if (convertView == null) { 
      convertView = mInflater.inflate(R.layout.imagelayout_2lines, null); 
      holder = new ViewHolder(); 
      holder.text = (TextView) convertView.findViewById(R.id.managementObjectText); 
      holder.subtext = (TextView) convertView.findViewById(R.id.managementObjectSubText); 
      holder.icon = (ImageView) convertView.findViewById(R.id.managementObjectIcon); 
      convertView.setTag(holder); 
     } 
     else { 
      holder = (ViewHolder) convertView.getTag(); 
     } 

     holder.text.setText(UserList.get(position).getFirstName()); 
     holder.subtext.setText(UserList.get(position).getLastName()); 
     holder.icon.setImageResource(R.drawable.user); 

     return convertView; 
    } 

    static class ViewHolder { 
     TextView text; 
     TextView subtext; 
     ImageView icon; 
    } 

    @Override 
    public Filter getFilter() { 
     return null; 
    } 
} 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.adobjectlist); 
    Bundle extras = getIntent().getExtras(); 

    SearchText = (EditText) findViewById(R.id.SearchBox);  
    SearchText.addTextChangedListener(filterTextWatcher); 

    objectListView = (ListView) findViewById(R.id.ObjectList); 
    objectListView.setOnItemClickListener(Item_Click); 
    adapter = new EfficientAdapter(this); 
    ComputerName = extras.getString("COMPUTER_NAME"); 

    //Get User list from webservice 
    ShowUsers(); 
} 

Voici la classe User:

public class User { 
    private int UserId; 
    private String FirstName; 
    private String LastName; 

    public int getUserId() { 
     return UserId; 
    } 
    public void setUserId(int UserId) { 
     this.UserId = UserId; 
    } 
    public String getFirstName() { 
     return FirstName; 
    } 
    public void setFirstName(String FirstName) { 
     this.FirstName = FirstName; 
    } 
    public String getLastName() { 
     return LastName; 
    } 
    public void setLastName(String LastName) { 
     this.LastName = LastName; 
    } 
} 

Répondre

70

Vous devez faire quelques petites choses:

1) Dans votre activité , enregistrez un écouteur de modification de texte sur votre EditText contenant la valeur entrée par l'utilisateur:

mSearchValue.addTextChangedListener (searchTextWatcher);

2) Créez votre searchTextWatcher et faites-le faire quelque chose:

private TextWatcher searchTextWatcher = new TextWatcher() { 
    @Override 
     public void onTextChanged(CharSequence s, int start, int before, int count) { 
      // ignore 
     } 

     @Override 
     public void beforeTextChanged(CharSequence s, int start, int count, int after) { 
      // ignore 
     } 

     @Override 
     public void afterTextChanged(Editable s) { 
      Log.d(Constants.TAG, "*** Search value changed: " + s.toString()); 
      adapter.getFilter().filter(s.toString()); 
     } 
    }; 

3) Remplacer getFilter() dans votre adaptateur personnalisé et faites-filtrer les résultats et aviser le listview que l'ensemble de données a changé.

@Override 
    public Filter getFilter() { 
     return new Filter() { 
      @SuppressWarnings("unchecked") 
      @Override 
      protected void publishResults(CharSequence constraint, FilterResults results) { 
       Log.d(Constants.TAG, "**** PUBLISHING RESULTS for: " + constraint); 
       myData = (List<MyDataType>) results.values; 
       MyCustomAdapter.this.notifyDataSetChanged(); 
      } 

      @Override 
      protected FilterResults performFiltering(CharSequence constraint) { 
       Log.d(Constants.TAG, "**** PERFORM FILTERING for: " + constraint); 
       List<MyDataType> filteredResults = getFilteredResults(constraint); 

       FilterResults results = new FilterResults(); 
       results.values = filteredResults; 

       return results; 
      } 
     }; 
    } 
+4

Pourquoi dites-vous "override" dans 3)? BaseAdapter n'a pas getFilter(). – Ixx

+4

Bonne prise - pas nécessaire. Ma classe Adapter personnalisée (héritant en réalité de AmazingAdapter du widget Open Source AmazingListView) implémente l'interface android.widget.Filterable qui requiert getFilter(). – DustinB

+24

[Ce tutoriel] (http://www.survivingwithandroid.com/2012/10/android-listview-custom-filter-and.html) m'a aidé. La réponse avait des choses peu claires, ou peut-être que je ne l'ai pas lu avec suffisamment de concentration. – Sufian

16

Voici un exemple intéressant

public Filter getFilter() { 
    return new Filter() { 

     @Override 
     protected FilterResults performFiltering(CharSequence constraint) { 
      final FilterResults oReturn = new FilterResults(); 
      final ArrayList<station> results = new ArrayList<station>(); 
      if (orig == null) 
       orig = items; 
      if (constraint != null) { 
       if (orig != null && orig.size() > 0) { 
        for (final station g : orig) { 
         if (g.getName().toLowerCase() 
           .contains(constraint.toString())) 
          results.add(g); 
        } 
       } 
       oReturn.values = results; 
      } 
      return oReturn; 
     } 

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

public void notifyDataSetChanged() { 
    super.notifyDataSetChanged(); 
    notifyChanged = true; 
} 
+1

excellente approche en utilisant la variable ORIG pour enregistrer l'instance initiale. Ça a bien marché, merci. – radhoo

+0

Merci beaucoup, c'est ce que je cherchais: D +1! – Tenaciousd93

2

Pour ceux qui ne ont pas besoin de l'interface Filterable, il y a une solution beaucoup plus simple. Cela gère également notifyDataSetChanged() correctement où les autres solutions échouent. Notez que vous devez ajouter une fonction getArray() au BaseAdapter qui renvoie simplement l'objet tableau qui a été transmis au constructeur.

public abstract class BaseFilterAdapter<T> extends BaseAdapter<T> { 

    private List<T> original; 
    private String lastFilter; 

    public BaseFilterAdapter(Context context, List<T> array) { 
     super(context, new LinkedList<T>()); 
     original = array; 
     filter(""); 
    } 

    protected abstract Boolean predicate(T element, String filter); 

    public void filter(String filter) { 
     lastFilter = filter; 
     super.getArray().clear(); 
     for (T element : original) 
      if (predicate(element, filter)) 
       super.getArray().add(element); 
     super.notifyDataSetChanged(); 
    } 

    @Override 
    public List<T> getArray() { 
     return original; 
    } 

    @Override 
    public void notifyDataSetChanged() { 
     filter(lastFilter); 
    } 
} 
+0

Il n'y a pas de méthode getArray à redéfinir dans ma classe BaseAdapter. –

1

override Ajouter toString sur votre classe de base. Par exemple

@Override 
public String toString() { 
    return this.name; 
} 

Ci-dessus crée votre liste de chaînes sous forme de liste. Donc vous pouvez utiliser:

your_edit_text.addTextChangedListener(new TextWatcher() { 

    @Override 
    public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { 
     YourActivity.this.YourAdapter.getFilter().filter(arg0); 
    } 

    @Override 
    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { 
    } 

    @Override 
    public void afterTextChanged(Editable arg0) {  
    } 
}); 
Questions connexes