2012-01-10 64 views
0

我在Internet上找到了一个按字母顺序对ListView进行排序的代码,与Android联系人一样。 它工作完美,但是当我实现一个搜索过滤器时,我的光标被关闭。如何正确实施?在SimpleCursorAdapter中访问关闭的光标

看到代码:

适配器:

/** 
* 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) { 
     } 
    }; 

}

当我运行的活动,ListView控件显示按字母顺序排列的列表。但是,当我开始打字的EditText的东西,会出现以下异常:

例外:

2月1日至10日:58:51.244:E/AndroidRuntime(1090): android.database.StaleDataException:访问关闭的光标

如何在不关闭游标的情况下获得ListView中发生的搜索过滤器函数?

+0

检查LogCat以查看异常的原因。这将有助于在代码中找到位置。 我相信问题是,你没有关闭游标。问题可能是您的FilterQueryProvider:您正在重置mCursor字段,而不关闭该字段引用的前一个游标。 – Stefan 2012-01-10 07:52:35

+0

错误发生在方法的返回“getPositionForSection(int section)”的适配器 – 2012-01-10 12:31:51

+0

我试图在返回游标之前关闭前一个游标FilterQueryProvider没有工作。 =/ – 2012-01-10 13:07:52

回答

0

对于任何遇到此问题的人,另一个原因可以是AlphabetIndexer游标引用。

如果您将AlphabetIndexer与CursorAdapter和CursorLoader一起使用,则应该重写适配器的swapCursor()方法并调用AlphabetIndexer的setCursor()方法。 AlphabetIndexer保存给它的构造函数的游标引用,当你的CursorLoader用新游标替换旧游标时,你也应该更新它。

@Override 
public Cursor swapCursor(Cursor newCursor) { 
    alphabetIndexer.setCursor(newCursor); 
    return super.swapCursor(newCursor); 
} 
+0

当'cursor'被关闭时,它会继续** error **。通常关闭的游标可以通过'swapCursor()'传递 – 2015-04-22 21:52:06