2010-09-22 11 views
11

Por el momento necesito filtrar un Cursor/CursorAdapter para mostrar solo las filas que coincidan con una condición específica en el ListView. No quiero volver a consultar el db todo el tiempo. Solo quiero filtrar el Cursor que obtuve al consultar el DB.¿Filtrar un cursor de la manera correcta?

he visto la pregunta: Filter rows from Cursor so they don't show up in ListView

Pero no entiendo cómo hacer el filtrado por overwritting los métodos de "mover" en mi CursorWrapper. Un ejemplo sería bueno.

Muchas gracias.

+0

¿Realmente no hay solución para ese problema? –

Respuesta

19

ACTUALIZACIÓN:

he reescrito la fuente y mi empleador ha hecho disponible como software de código abierto: https://github.com/clover/android-filteredcursor

No es necesario para reemplazar todos los métodos se mueven en CursorWrapper, que haces Sin embargo, debe sobrescribir un grupo debido al diseño de la interfaz del Cursor. Vamos a suponer que desea filtrar fila # 2 y # 4 de un cursor 7 fila, hacer una clase que se extiende CursorWrapper y anular estos métodos, así:

private int[] filterMap = new int[] { 0, 1, 3, 5, 6 }; 
private int mPos = -1; 

@Override 
public int getCount() { return filterMap.length } 

@Override 
public boolean moveToPosition(int position) { 
    // Make sure position isn't past the end of the cursor 
    final int count = getCount(); 
    if (position >= count) { 
     mPos = count; 
     return false; 
    } 

    // Make sure position isn't before the beginning of the cursor 
    if (position < 0) { 
     mPos = -1; 
     return false; 
    } 

    final int realPosition = filterMap[position]; 

    // When moving to an empty position, just pretend we did it 
    boolean moved = realPosition == -1 ? true : super.moveToPosition(realPosition); 
    if (moved) { 
     mPos = position; 
    } else { 
     mPos = -1; 
    } 
    return moved; 
} 

@Override 
public final boolean move(int offset) { 
    return moveToPosition(mPos + offset); 
} 

@Override 
public final boolean moveToFirst() { 
    return moveToPosition(0); 
} 

@Override 
public final boolean moveToLast() { 
    return moveToPosition(getCount() - 1); 
} 

@Override 
public final boolean moveToNext() { 
    return moveToPosition(mPos + 1); 
} 

@Override 
public final boolean moveToPrevious() { 
    return moveToPosition(mPos - 1); 
} 

@Override 
public final boolean isFirst() { 
    return mPos == 0 && getCount() != 0; 
} 

@Override 
public final boolean isLast() { 
    int cnt = getCount(); 
    return mPos == (cnt - 1) && cnt != 0; 
} 

@Override 
public final boolean isBeforeFirst() { 
    if (getCount() == 0) { 
     return true; 
    } 
    return mPos == -1; 
} 

@Override 
public final boolean isAfterLast() { 
    if (getCount() == 0) { 
     return true; 
    } 
    return mPos == getCount(); 
} 

@Override 
public int getPosition() { 
    return mPos; 
} 

Ahora la parte interesante es la creación de las filterMap, eso depende para ti.

+3

Tomando de su respuesta, parece una forma natural de crear el filterMap es tomar un Cursor en el constructor, usar una condición de filtro en cada entrada para determinar si agrega esa fila a filterMap u omítela. Ahora tiene un Cursor que solo devolverá las entradas que satisfacen la condición. –

+0

¡Gracias, eres un salvador de vidas! :) – Sadegh

+0

¡Amigo, eres un superhombre de mi día!))) Muchas gracias))) – UnknownJoe

1

Estaba buscando algo similar, en mi caso quería filtrar elementos en función de una comparación de cadenas. Encontré esta esencia https://gist.github.com/ramzes642/5400792, que funciona bien a menos que empieces a jugar con la posición del cursor. Así que encontré una respuesta saturinina, la suya respeta la API de posición pero solo necesita algunos ajustes para filtrar en base al cursor, así que fusioné ambas. Puede cambiar el código para encajarlo: https://gist.github.com/rfreitas/ab46edbdc41500b20357

import java.text.Normalizer; 

import android.database.Cursor; 
import android.database.CursorWrapper; 
import android.util.Log; 


//by Ricardo [email protected] 
//ref: https://gist.github.com/ramzes642/5400792 (the position retrieved is not correct) 
//ref: http://stackoverflow.com/a/7343721/689223 (doesn't do string filtering) 
//the two code bases were merged to get the best of both worlds 
//also added was an option to remove accents from UTF strings 
public class FilterCursorWrapper extends CursorWrapper { 
    private static final String TAG = FilterCursorWrapper.class.getSimpleName(); 
    private String filter; 
    private int column; 
    private int[] filterMap; 
    private int mPos = -1; 
    private int mCount = 0; 

    public FilterCursorWrapper(Cursor cursor,String filter,int column) { 
     super(cursor); 
     this.filter = deAccent(filter).toLowerCase(); 
     Log.d(TAG, "filter:"+this.filter); 
     this.column = column; 
     int count = super.getCount(); 

     if (!this.filter.isEmpty()) { 
      this.filterMap = new int[count]; 
      int filteredCount = 0; 
      for (int i=0;i<count;i++) { 
       super.moveToPosition(i); 
       if (deAccent(this.getString(this.column)).toLowerCase().contains(this.filter)){ 
        this.filterMap[filteredCount] = i; 
        filteredCount++; 
       } 
      } 
      this.mCount = filteredCount; 
     } else { 
      this.filterMap = new int[count]; 
      this.mCount = count; 
      for (int i=0;i<count;i++) { 
       this.filterMap[i] = i; 
      } 
     } 

     this.moveToFirst(); 
    } 



    public int getCount() { return this.mCount; } 

    @Override 
    public boolean moveToPosition(int position) { 
     Log.d(TAG,"moveToPosition:"+position); 
     // Make sure position isn't past the end of the cursor 
     final int count = getCount(); 
     if (position >= count) { 
      mPos = count; 
      return false; 
     } 
     // Make sure position isn't before the beginning of the cursor 
     if (position < 0) { 
      mPos = -1; 
      return false; 
     } 
     final int realPosition = filterMap[position]; 
     // When moving to an empty position, just pretend we did it 
     boolean moved = realPosition == -1 ? true : super.moveToPosition(realPosition); 
     if (moved) { 
      mPos = position; 
     } else { 
      mPos = -1; 
     } 
     Log.d(TAG,"end moveToPosition:"+position); 
     return moved; 
    } 
    @Override 
    public final boolean move(int offset) { 
     return moveToPosition(mPos + offset); 
    } 
    @Override 
    public final boolean moveToFirst() { 
     return moveToPosition(0); 
    } 
    @Override 
    public final boolean moveToLast() { 
     return moveToPosition(getCount() - 1); 
    } 
    @Override 
    public final boolean moveToNext() { 
     return moveToPosition(mPos + 1); 
    } 
    @Override 
    public final boolean moveToPrevious() { 
     return moveToPosition(mPos - 1); 
    } 
    @Override 
    public final boolean isFirst() { 
     return mPos == 0 && getCount() != 0; 
    } 
    @Override 
    public final boolean isLast() { 
     int cnt = getCount(); 
     return mPos == (cnt - 1) && cnt != 0; 
    } 
    @Override 
    public final boolean isBeforeFirst() { 
     if (getCount() == 0) { 
      return true; 
     } 
     return mPos == -1; 
    } 
    @Override 
    public final boolean isAfterLast() { 
     if (getCount() == 0) { 
      return true; 
     } 
     return mPos == getCount(); 
    } 
    @Override 
    public int getPosition() { 
     return mPos; 
    } 

    //added by Ricardo 
    //ref: http://stackoverflow.com/a/22612054/689223 
    //other: http://stackoverflow.com/questions/8523631/remove-accents-from-string 
    //other: http://stackoverflow.com/questions/15190656/easy-way-to-remove-utf-8-accents-from-a-string 
    public static String deAccent(String str) { 
     //return StringUtils.stripAccents(str);//this method from apache.commons respects chinese characters, but it's slower than flattenToAscii 
     return flattenToAscii(str); 
    } 

    //ref: http://stackoverflow.com/a/15191508/689223 
    //this is the fastest method using the normalizer found yet, the ones using Regex are too slow 
    public static String flattenToAscii(String string) { 
     char[] out = new char[string.length()]; 
     string = Normalizer.normalize(string, Normalizer.Form.NFD); 
     int j = 0; 
     for (int i = 0, n = string.length(); i < n; ++i) { 
      char c = string.charAt(i); 
      int type = Character.getType(c); 
      if (type != Character.NON_SPACING_MARK){ 
       out[j] = c; 
       j++; 
      } 
     } 
     return new String(out); 
    } 
} 
0

He comparado iteración a través del cursor 1790 entradas de consulta en contra cursor con REGEXP y es 1 min 15 seg 15 seg en contra.

Utilice REGEXP - es mucho más rápido.

Cuestiones relacionadas