在SimpleCursorAdapter中访问关闭的游标

时间:2012-01-10 06:10:56

标签: android cursor adapter

我在互联网上找到了一个代码,用于按字母顺序对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中发生的搜索过滤器功能?

1 个答案:

答案 0 :(得分:0)

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

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

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