2012-01-10 67 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