java - setadapter - Extendiendo ArrayAdapter en Android
listview android studio (4)
Necesito anular un método getFilter()
de la clase ArrayAdapter
y encontré el código fuente de aquí en el github
//package name
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.content.Context;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.Filterable;
public class CustomAdapter<T> extends ArrayAdapter<T> implements Filterable{
private ArrayList<T> mOriginalValues;
private List<T> mObjects;
private CustomFilter mFilter;
private final Object mLock = new Object();
public CustomAdapter(Context context, int textViewResourceId, T[] objects) {
super(context, textViewResourceId, objects);
mObjects = Arrays.asList(objects);
// TODO Auto-generated constructor stub
}
@Override
public Filter getFilter() {
// TODO Auto-generated method stub
if (mFilter == null) {
mFilter = new CustomFilter();
}
return mFilter;
}
private class CustomFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
Log.d("bajji", "its ---> " + prefix);
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<T>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<T>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<T>();
final ArrayList<T> approxValues = new ArrayList<T>();
final ArrayList<T> secondApproxValues = new ArrayList<T>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final String valueText = value.toString().toLowerCase();
boolean flag = true;
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
flag = false;
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
flag = false;
break;
}
}
}
if(flag) {
if(approxMatch(valueText, prefixString) <= 3) { //change the stuff and do a levi work
approxValues.add(value);
}
else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if(approxMatch(words[k], prefixString) <= 3) {
//leve work
secondApproxValues.add(value);
break;
}
}
}
}
}
newValues.addAll(approxValues);
newValues.addAll(secondApproxValues);
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
private int approxMatch (String s, String t) {
// an approxmimate string matching algo
return p;
}
}
El problema es que el método getFilter tiene un objeto de una clase interna privada ArrayFilter
que tiene un método peformFiltering
y necesito poner un código diferente allí, así que tengo que sobrescribir la clase. Y me sale una excepción en el método.
En la clase derivada que extiende ArrayAdapter, creé una clase interna privada que es similar a ArrayFilter
y la llamé MyFilter
y obtengo la misma excepción nuevamente en el método performFiltering
.
Encontré una solución para resolver mi problema. Copié todo el código en la clase ArrayAdapter
y creé una nueva clase llamada MyAdapter
y modifiqué algo de código dentro de la clase interna ArrayFilter
y la aplicación funciona de la manera que yo quería. Pero siento que no es la mejor solución.
Android tiene varios niveles de api, de modo que si el adaptador de arreglo se cambia en un nivel de api diferente, entonces tengo que agregar esos cambios en mis códigos. Así que siento que la mejor manera es extender la clase ArrayAdapter
para crear MyAdapter
lugar de simplemente copiar y pegar el código de ArrayAdapter
¿Cómo puedo anular la clase privada interna de una clase padre?
Editar: La excepción que consigo ..
Edit2: Ahora agregué el código completo en la pregunta. y funciona perfectamente si copio y edito el adaptador de matriz ... el problema es solo cuando extiendo ... !! Ahora el código y la búsqueda están funcionando perfectamente. Lo verifiqué con Log.i
.. pero la lista desplegable para la sugerencia de autocompletar en la interfaz de usuario no está funcionando. Solo obtengo el primer carácter, escribo el siguiente filtrado de caracteres, pero no se realiza la actualización de la interfaz de usuario.
Actualizar:
Debería anular el método toString()
en su clase T
y devolver la cadena de criterio de filtro.
Por ejemplo,
class T {
String firstName;
String lastName;
...
...
@override
String toString() {
return firstName + lastName;
}
}
De forma predeterminada, la implementación toString()
de la clase Object
devuelve getClass().getName() + ''@'' + Integer.toHexString(hashCode())
Original:
La referencia de la lista de matriz se debe pasar al constructor ArrayAdapter
. Creo que no está haciendo esto y, por lo tanto, no puede obtener una lista para el filtrado.
En este ejemplo de código de objects
referencia de matriz se pasa al super constructor.
public CustomAdapter(Context context, int tvResId, ArrayList<String> objects) {
super(context, textViewResourceId, objects);
this.objects = objects;
}
Si su filtrado se basa en las cadenas simples en los elementos de la lista, entonces puede implementar TextWatcher
como lo sugiere @Iiorry. Pero si el filtrado es un poco complejo, entonces también es necesario implementar Filterable
.
Después de recibir ayuda de la comunidad , eliminé la excepción y más tarde descubrí que las sugerencias que se devuelven no cambian realmente porque los mObjects
se devolvieron desde la clase super o la clase base. El problema era que había dos métodos públicos getCount()
y getItem(int position)
que obtienen el recuento y recuperan la lista de mObjects de la clase base. Así que solo tengo que agregar esos dos métodos en mi clase ...
public int getCount() {
return mObjects.size();
}
public T getItem(int position) {
return mObjects.get(position);
}
Ahora se mObjects
de la clase derivada. ¿Cuáles son los actualizados en la lista desplegable en la interfaz de usuario.
No soy un experto en java, pero esto resuelve mi problema. Si tiene alguna sugerencia para mejorar el código, agréguelo a los comentarios o no dude en editar la respuesta.
No necesita implementar su propio filtro ... simplemente anule getFilter como sugirió @Waqas.
Esto es lo que hice y funciona. Siéntase libre de ajustarlo a sus necesidades ...
Espero que te ayude.
El EditText (searchFilder):
searchFilter.addTextChangedListener(mOnSearchBoxTextChanged);
private TextWatcher mOnSearchBoxTextChanged = new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
mAdapter.getFilter().filter(s.toString());
}
};
El adaptador getFilter ():
class MyAdapter extends BaseAdapter implements Filterable {
//the rest of the adapter....
@Override
public Filter getFilter() {
if (newFilter == null) {
newFilter = new Filter() {
@Override
protected void publishResults(CharSequence prefix,
FilterResults results) {
CLog.logD( "Friends list filtered");
mAdapter.notifyDataSetChanged();
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
constraint = constraint.toString().toLowerCase();
filteredFriendList = new ArrayList<FriendsListItemView>();
if (constraint!= null && constraint.toString().length() > 0) {
for (int i = 0; i < friendsList.size(); i++) {
FriendsListItemView newFriend = friendsList.get(i);
String name = newFriend.userName.getText().toString();
if (name.toLowerCase().contains(constraint)) {
filteredFriendList.add(newFriend);
}
}
} else {
if (filteredFriendList.size() == 0) {
filteredFriendList = friendsList;
}
}
FilterResults newFilterResults = new FilterResults();
newFilterResults.count = filteredFriendList.size();
newFilterResults.values = filteredFriendList;
return newFilterResults;
}
};
}
return newFilter;
}
}
Quizás esto pueda ayudarte:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.content.Context;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Filter;
public class CustomAdapter<T> extends ArrayAdapter<T> {
private final ArrayList<T> mOriginalValues;
private List<T> mObjects;
private CustomFilter mFilter;
private final Object mLock = new Object();
public CustomAdapter(Context context, int textViewResourceId, T[] objects) {
super(context, textViewResourceId, objects);
mObjects = Arrays.asList(objects);
mOriginalValues = (ArrayList<T>) Arrays.asList(objects);
// TODO Auto-generated constructor stub
}
@Override
public Filter getFilter() {
// TODO Auto-generated method stub
if (mFilter == null) {
mFilter = new CustomFilter();
}
return mFilter;
}
private class CustomFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
Log.d("bajji", "its ---> " + prefix);
if (prefix == null || prefix.toString().trim().length() == 0) {
ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<T>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<T>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<T>();
final ArrayList<T> approxValues = new ArrayList<T>();
final ArrayList<T> secondApproxValues = new ArrayList<T>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final String valueText = value.toString().toLowerCase();
boolean flag = true;
// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
flag = false;
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
flag = false;
break;
}
}
}
if(flag) {
if(approxMatch(valueText, prefixString) <= 3) { //change the stuff and do a levi work
approxValues.add(value);
}
else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
// Start at index 0, in case valueText starts with space(s)
for (int k = 0; k < wordCount; k++) {
if(approxMatch(words[k], prefixString) <= 3) {
//leve work
secondApproxValues.add(value);
break;
}
}
}
}
}
newValues.addAll(approxValues);
newValues.addAll(secondApproxValues);
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
mObjects = (List<T>) results.values;
notifyDataSetChanged();
clear();
for(T tmp : mObjects){
add(tmp);
}
notifyDataSetChanged();
}
}
private int approxMatch (String s, String t) {
// an approxmimate string matching algo
return p;
}
}
Ahora, en el método getView
del adaptador, debe consultar el objeto mObjects para obtener los valores recientes disponibles para ListView