2010-09-22 70 views
11

此刻我需要过滤Cursor/CursorAdapter以仅显示符合ListView中特定条件的行。我不想总是重新查询数据库。我只想过滤从查询数据库中获得的光标。以正确的方式过滤光标?

我已经看到了问题:Filter rows from Cursor so they don't show up in ListView

但我不知道如何在我的CursorWrapper overwritting“移动”的方法做滤波。一个例子会很好。

非常感谢。

+0

这个问题真的没有解决方案吗? – 2010-10-08 05:30:22

回答

19

UPDATE:

我已经重写源和我的雇主取得了它可作为开源软件:https://github.com/clover/android-filteredcursor

你并不需要覆盖CursorWrapper的所有移动方法,你做由于Cursor接口的设计,需要重写一堆。让我们假设你想筛选出7行光标列#2,#4,使扩展CursorWrapper一个类并重写这些方法,像这样:

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

现在最有趣的部分是创建filterMap,这是达给你。

+3

从你的答案中,似乎创建filterMap的一种自然方法是将Cursor放入构造函数中,对每个条目使用过滤条件以确定将该行添加到filterMap还是跳过它。现在你有一个Cursor只会返回满足条件的条目。 – 2012-06-04 17:27:14

+0

谢谢,你是一位救世主! :) – Sadegh 2013-11-18 13:27:54

+0

伙计,你是我一天的超人!)))非常感谢))) – UnknownJoe 2014-03-23 16:45:46

1

我正在寻找类似的东西,在我的情况下,我想根据字符串比较过滤项目。我发现这个要点https://gist.github.com/ramzes642/5400792,它工作正常,除非你开始玩游标的位置。所以我找到了satur9nine的答案,他的人尊重位置API,但只需要根据光标进行一些滤波调整,所以我合并了这两个。你可以改变你的代码,以适应它: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

我比较了通过光标1790项针对迭代查询在游标REGEXP,这是1分钟与15秒15秒。

使用REGEXP - 它快得多。