Filtrado de ListView con adaptador personalizado (objeto)

Estoy tratando de implementar el filtrado de un ListView que utiliza un adaptador de objeto personalizado, pero no puedo encontrar ninguna muestra útil. El código incluido está muy simplificado, así que no, tenga en cuenta que no puedo usar un ArrayAdapter regular. Tengo un EditText encima de ListView, y cuando el usuario ingresa texto en el widget EditText me gustaría filtrar el ListView por el texto escrito en EditText. ¡Cualquier sugerencia sería muy apreciada!

Aquí está el fragmento de la clase de actividad:

public class management_objects extends Activity { private static List 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(); } 

Aquí está la clase de usuario:

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

Necesitas hacer algunas cosas:

1) En su actividad, regístrese para un oyente de cambio de texto en su EditText que contiene el valor que ingresa el usuario:

mSearchValue.addTextChangedListener (searchTextWatcher);

2) Crea tu searchTextWatcher y haz que haga algo:

 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) Anule getFilter () en su adaptador personalizado y haga que filtre los resultados y notifique a la vista de lista que el conjunto de datos ha cambiado.

  @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) results.values; MyCustomAdapter.this.notifyDataSetChanged(); } @Override protected FilterResults performFiltering(CharSequence constraint) { Log.d(Constants.TAG, "**** PERFORM FILTERING for: " + constraint); List filteredResults = getFilteredResults(constraint); FilterResults results = new FilterResults(); results.values = filteredResults; return results; } }; } 

Aquí un ejemplo interesante

 public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { final FilterResults oReturn = new FilterResults(); final ArrayList results = new ArrayList(); 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) results.values; notifyDataSetChanged(); } }; } public void notifyDataSetChanged() { super.notifyDataSetChanged(); notifyChanged = true; } 

Para aquellos que no necesitan la interfaz Filterable , hay una solución mucho más simple. Esto también maneja notifyDataSetChanged() correctamente donde las otras soluciones fallan. Tenga en cuenta que debe agregar una función getArray() al BaseAdapter que solo devuelve el objeto de matriz que se pasó al constructor.

 public abstract class BaseFilterAdapter extends BaseAdapter { private List original; private String lastFilter; public BaseFilterAdapter(Context context, List array) { super(context, new LinkedList()); 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 getArray() { return original; } @Override public void notifyDataSetChanged() { filter(lastFilter); } } 

Agregue aString la anulación en su clase base. Por ejemplo

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

Arriba hace su lista como lista de cuerdas. Entonces puedes usar:

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