2011-04-25 28 views
62

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!Filtrado ListView con adaptador personalizado (objeto)

Aquí es el fragmento de la clase de actividad:

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

Aquí está la clase 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; 
    } 
} 

Respuesta

70

Es necesario hacer algunas cosas:

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

mSearchValue.addTextChangedListener (searchTextWatcher);

2) Crear su searchTextWatcher y lo han de hacer 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) Anular getFilter() en el adaptador de costumbre y tiene que filtrar los resultados y notificar 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<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

¿Por qué dices "anular" en 3)? BaseAdapter no tiene getFilter(). – Ixx

+4

Buena captura - no es necesario. Mi clase adaptadora personalizada (en realidad hereda de AmazingAdapter desde el widget de código abierto de AmazingListView) implementa la interfaz android.widget.Filterable que requiere getFilter(). – DustinB

+24

[Este tutorial] (http://www.survivingwithandroid.com/2012/10/android-listview-custom-filter-and.html) me ayudó. La respuesta tenía algunas cosas poco claras, o tal vez no lo leí con suficiente concentración. – Sufian

16

Aquí un ejemplo interesante

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

excelente enfoque en el uso de la variable ORIG para guardar la instancia inicial. Funcionó bien, gracias. – radhoo

+0

Muchas gracias, es lo que estaba buscando: D +1! – Tenaciousd93

2

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

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

no hay método getArray para anular en mi clase BaseAdapter. –

1

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

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

Above hace su lista como lista de cadenas. Para que pueda 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) {  
    } 
}); 
Cuestiones relacionadas