我在互联网上找到了一个代码,用于按字母顺序对ListView
进行排序,作为Android联系人。
它工作得很好,但是当我实现搜索过滤器时,我的光标已关闭。如何正确实现?
参见代码:
适配器:
/**
* CursorAdapter that uses an AlphabetIndexer widget to keep track of the section indicies.
* These are the positions where we want to show a section header showing the respective alphabet letter.
* @author Eric
*
*/
public class OrdemAlfabeticaAdapter extends SimpleCursorAdapter implements SectionIndexer{
private static final int TYPE_HEADER = 1;
private static final int TYPE_NORMAL = 0;
private static final int TYPE_COUNT = 2;
private AlphabetIndexer indexer;
private int[] usedSectionNumbers;
private Map<Integer, Integer> sectionToOffset;
private Map<Integer, Integer> sectionToPosition;
private Context context;
public OrdemAlfabeticaAdapter(Context context, int layout, Cursor c, String coluna,
String[] from, int[] to) {
super(context, layout, c, from, to);
this.context = context;
indexer = new AlphabetIndexer(c, c.getColumnIndexOrThrow(coluna), "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
sectionToPosition = new TreeMap<Integer, Integer>(); //use a TreeMap because we are going to iterate over its keys in sorted order
sectionToOffset = new HashMap<Integer, Integer>();
final int count = super.getCount();
int i;
//temporarily have a map alphabet section to first index it appears
//(this map is going to be doing somethine else later)
for (i = count - 1 ; i >= 0; i--){
sectionToPosition.put(indexer.getSectionForPosition(i), i);
}
i = 0;
usedSectionNumbers = new int[sectionToPosition.keySet().size()];
//note that for each section that appears before a position, we must offset our
//indices by 1, to make room for an alphabetical header in our list
for (Integer section : sectionToPosition.keySet()){
sectionToOffset.put(section, i);
usedSectionNumbers[i] = section;
i++;
}
//use offset to map the alphabet sections to their actual indicies in the list
for(Integer section: sectionToPosition.keySet()){
sectionToPosition.put(section, sectionToPosition.get(section) + sectionToOffset.get(section));
}
}
@Override
public int getCount() {
if (super.getCount() != 0){
//sometimes your data set gets invalidated. In this case getCount()
//should return 0 and not our adjusted count for the headers.
//The only way to know if data is invalidated is to check if
//super.getCount() is 0.
return super.getCount() + usedSectionNumbers.length;
}
return 0;
}
@Override
public Object getItem(int position) {
if (getItemViewType(position) == TYPE_NORMAL){//we define this function in the full code later
//if the list item is not a header, then we fetch the data set item with the same position
//off-setted by the number of headers that appear before the item in the list
return super.getItem(position - sectionToOffset.get(getSectionForPosition(position)) - 1);
}
return null;
}
@Override
public int getPositionForSection(int section) {
if (! sectionToOffset.containsKey(section)){
//This is only the case when the FastScroller is scrolling,
//and so this section doesn't appear in our data set. The implementation
//of Fastscroller requires that missing sections have the same index as the
//beginning of the next non-missing section (or the end of the the list if
//if the rest of the sections are missing).
//So, in pictorial example, the sections D and E would appear at position 9
//and G to Z appear in position 11.
int i = 0;
int maxLength = usedSectionNumbers.length;
//linear scan over the sections (constant number of these) that appear in the
//data set to find the first used section that is greater than the given section, so in the
//example D and E correspond to F
while (i < maxLength && section > usedSectionNumbers[i]){
i++;
}
if (i == maxLength) return getCount(); //the given section is past all our data
return indexer.getPositionForSection(usedSectionNumbers[i]) + sectionToOffset.get(usedSectionNumbers[i]);
}
return indexer.getPositionForSection(section) + sectionToOffset.get(section); // *** It is this line that the error occurred ***
}
@Override
public int getSectionForPosition(int position) {
int i = 0;
int maxLength = usedSectionNumbers.length;
//linear scan over the used alphabetical sections' positions
//to find where the given section fits in
while (i < maxLength && position >= sectionToPosition.get(usedSectionNumbers[i])){
i++;
}
return usedSectionNumbers[i-1];
}
@Override
public Object[] getSections() {
return indexer.getSections();
}
//nothing much to this: headers have positions that the sectionIndexer manages.
@Override
public int getItemViewType(int position) {
if (position == getPositionForSection(getSectionForPosition(position))){
return TYPE_HEADER;
} return TYPE_NORMAL;
}
@Override
public int getViewTypeCount() {
return TYPE_COUNT;
}
//return the header view, if it's in a section header position
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final int type = getItemViewType(position);
if (type == TYPE_HEADER){
if (convertView == null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
// convertView = getLayoutInflater().inflate(R.layout.header, parent, false);
convertView = inflater.inflate(R.layout.cabecalho_divisao_alfabetica, parent, false);
}
((TextView)convertView.findViewById(R.id.header)).setText((String)getSections()[getSectionForPosition(position)]);
return convertView;
}
return super.getView(position - sectionToOffset.get(getSectionForPosition(position)) - 1, convertView, parent);
}
//these two methods just disable the headers
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int position) {
if (getItemViewType(position) == TYPE_HEADER){
return false;
}
return true;
}
}
我的活动:
public class TesteActivity extends ListActivity {
SimpleCursorAdapter adapter;
private Cursor mCursor;
private DBHelper dbHelper;
private SQLiteDatabase db;
private EditText filterEditText;
@Override
public void onCreate(Bundle savedInstanceState) {
try {
super.onCreate(savedInstanceState);
setContentView(R.layout.proprietario_busca);
dbHelper = new DBHelper(getApplicationContext(), Constantes.BANCO_DE_DADOS_NOME, Constantes.BANCO_DE_DADOS_VERSAO);
db = dbHelper.getReadableDatabase();
mCursor = db.query("pessoa", new String[] {"_id","nome"}, null, null, null, null, null);
startManagingCursor(mCursor);
adapter = new OrdemAlfabeticaAdapter(this, R.layout.list_item_proprietario,
mCursor, ProprietarioProvider.Columns.NOME, new String[] { "nome" },
new int[]{R.id.list_item_proprietario_nome});
setListAdapter(adapter);
filterEditText = (EditText) findViewById(R.id.busca_proprietario_campo_busca);
filterEditText.addTextChangedListener(filterTextWatcher);
getListView().setTextFilterEnabled(true);
adapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(final CharSequence substring) {
return db.query("pessoa", new String[] {"_id","nome"}, "nome LIKE '%" + substring.toString() + "%'", null, null, null, null);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private TextWatcher filterTextWatcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
adapter.getFilter().filter(s.toString());
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
};
}
当我运行Activity时,ListView按字母顺序显示列表。但是当我开始在EditText中输入内容时,会出现以下异常:
例外:
01-10 02:58:51.244:E / AndroidRuntime(1090): android.database.StaleDataException:访问已关闭的游标
如何在不关闭光标的情况下获得ListView中发生的搜索过滤器功能?
答案 0 :(得分:0)
对于遇到此问题的任何人,另一个原因可能是AlphabetIndexer游标引用。
如果将AlphabetIndexer与CursorAdapter和CursorLoader一起使用,则应覆盖适配器的swapCursor()方法并调用AlphabetIndexer的setCursor()方法。 AlphabetIndexer保留给它的构造函数的游标的引用,当你的CursorLoader用新游标替换旧游标时你也应该更新它。
@Override
public Cursor swapCursor(Cursor newCursor) {
alphabetIndexer.setCursor(newCursor);
return super.swapCursor(newCursor);
}