在 SimpleCursorAdapter 中访问关闭的光标

发布于 2024-12-26 01:25:25 字数 9528 浏览 1 评论 0原文

我在互联网上找到了一个按字母顺序对ListView进行排序的代码,就像Android联系人一样。 它工作得很好,但是当我实现搜索过滤器时,我的光标被关闭。如何正确实施这一点?

请参阅代码:

Adapter:

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

My Activity:

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中发生的搜索过滤功能?

I found on the Internet a code to sort a ListView in alphabetical order, as the Android Contacts.
It worked perfectly, but when I implement a search filter, my cursor is closed. How to implement this correctly?

See the code:

Adapter:

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

My Activity:

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

}

When I run the Activity, the ListView displays the list in alphabetical order. But when I start typing something in EditText, the following exception appears:

Exception:

01-10 02:58:51.244: E/AndroidRuntime(1090):
android.database.StaleDataException: Access closed cursor

How do I get the search filter function that occurs in the ListView without closing the cursor?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

随心而道 2025-01-02 01:25:25

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

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

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

For anyone that come across this question, another reason can be the AlphabetIndexer cursor reference.

If you use AlphabetIndexer with CursorAdapter and CursorLoader, you should override swapCursor() method of adapter and call setCursor() method of AlphabetIndexer. AlphabetIndexer keeps a reference for cursor given to its constructor and you should also update it when your CursorLoader replaces the old cursor with new cursor.

@Override
public Cursor swapCursor(Cursor newCursor) {
    alphabetIndexer.setCursor(newCursor);
    return super.swapCursor(newCursor);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文