Android的ListView控件与SimpleCursorAdapter - CursorIndexOutOfBoundsException错误 [英] Android ListView with SimpleCursorAdapter - CursorIndexOutOfBoundsException error

查看:285
本文介绍了Android的ListView控件与SimpleCursorAdapter - CursorIndexOutOfBoundsException错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个的ListView AcitivityA 正在使用自定义的填充 SimpleCursorAdapter 名为 RecipeAdapter 。该适配器拥有的SQLite

数据

有位于的ListView ,用于过滤列表视图作为用户搜索的顶部有一个的EditText 视图一个食谱。当用户点击在经过过滤的ListView ActivityB 启动一个项目。

这一切完美的作品。然而,当用户presses的后退按钮恢复 ActivityB ,我碰到下面的错误。

 了java.lang.RuntimeException:无法恢复活动{ttj.android.quorn / ttj.android.quorn.RecipeActivity}:
java.lang.IllegalStateException:尝试重新查询已经关闭的游标android.database.sqlite.SQLiteCursor@418ae5d8

要解决这个问题,我修改了 onResume()从:

  ...
C = db.getCursor();
adapter.changeCursor(C);

  ....
光标光标= db.getCursor();
adapter.changeCursor(光标);

然后我得到下面的异常。在logcat中,这个问题在 DBHelper 的getId()方法产生。我已经加入 c.moveToFirst()在这个方法,但是这仍然没有解决问题。

 致命异常:主要
android.database.CursorIndexOutOfBoundsException:指数-1要求,尺寸为70
在android.database.AbstractCursor.checkPosition(AbstractCursor.java:400)
在android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
在android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50)
在ttj.android.quorn.DBHelper.getId(DBHelper.java:224)
在ttj.android.quorn.RecipeActivity $ RecipeHolder.populateFrom(RecipeActivity.java:650)
在ttj.android.quorn.RecipeActivity $ RecipeAdapter.bindView(RecipeActivity.java:572)
在android.support.v4.widget.CursorAdapter.getView(CursorAdapter.java:256)
在android.widget.AbsListView.obtainView(AbsListView.java:2214)
在android.widget.ListView.makeAndAddView(ListView.java:1774)
在android.widget.ListView.fillDown(ListView.java:672)
在android.widget.ListView.fillFromTop(ListView.java:732)
在android.widget.ListView.layoutChildren(ListView.java:1611)
在android.widget.AbsListView.onLayout(AbsListView.java:2044)
在android.view.View.layout(View.java:11418)
在android.view.ViewGroup.layout(ViewGroup.java:4224)
在android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
在android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
在android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
在android.view.View.layout(View.java:11418)
在android.view.ViewGroup.layout(ViewGroup.java:4224)
在android.widget.FrameLayout.onLayout(FrameLayout.java:431)
在android.view.View.layout(View.java:11418)
在android.view.ViewGroup.layout(ViewGroup.java:4224)
在android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
在android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
在android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
在android.view.View.layout(View.java:11418)
在android.view.ViewGroup.layout(ViewGroup.java:4224)
在android.widget.FrameLayout.onLayout(FrameLayout.java:431)
在android.view.View.layout(View.java:11418)
在android.view.ViewGroup.layout(ViewGroup.java:4224)
在android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1628)
在android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2585)
在android.os.Handler.dispatchMessage(Handler.java:99)
在android.os.Looper.loop(Looper.java:137)
在android.app.ActivityThread.main(ActivityThread.java:4507)
在java.lang.reflect.Method.invokeNative(本机方法)
在java.lang.reflect.Method.invoke(Method.java:511)
在com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:790)
在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
在dalvik.system.NativeStart.main(本机方法)

谁能帮我与我的问题?

下面是我的code:

的onCreate 光标填充的ListView 使用 c.getCursor 键,当用户过滤的ListView 通过的EditText c.getFilterCursor 被使用。

 公共类RecipeActivity扩展SherlockListActivity {私人DBHelper DB = NULL;
私人光标C = NULL;
私人RecipeAdapter适配器= NULL;
ListView控件的listContent;
私人的EditText filterText = NULL;@燮pressWarnings(德precation)
@覆盖
公共无效的onCreate(捆绑savedInstanceState){
    尝试{
        super.onCreate(savedInstanceState);
        的setContentView(R.layout.filter_list);        filterText =(EditText上)findViewById(R.id.search_box);
        filterText.addTextChangedListener(filterTextWatcher);        ListView控件的listContent = getListView();        DB =新DBHelper(本);
        db.createDataBase();
        db.openDataBase();        C = db.getCursor();        适配器=新RecipeAdapter(C);        listContent.setAdapter(适配器);        adapter.setFilterQueryProvider(新FilterQueryProvider(){
            公共光标runQuery(CharSequence的约束){
                //搜索状态,其名称以字母指定。
                C = db.getFilterCursor(约束);
                返回℃;
            }
        });        startManagingCursor(C);
    }赶上(IOException异常五){
        e.printStackTrace();
    }
}@覆盖
保护无效的onDestroy(){
    super.onDestroy();
    filterText.removeTextChangedListener(filterTextWatcher);
    db.close();
}
@燮pressWarnings(德precation)
@覆盖
保护无效onResume(){
    super.onResume();    光标光标= db.getCursor();
    adapter.changeCursor(光标);}
@覆盖
保护无效的onPause(){
    super.onPause();    adapter.notifyDataSetInvalidated();
    adapter.changeCursor(NULL);}
    私人TextWatcher filterTextWatcher =新TextWatcher(){    公共无效afterTextChanged(编辑S){
    }    公共无效beforeTextChanged(CharSequence中,诠释开始,诠释计数,
            INT后){
    }    公共无效之前onTextChanged(CharSequence中,诠释开始,诠释,
            诠释计数){        。adapter.getFilter()过滤器(S);
    }};

RecipeAdapter 内部类

 类RecipeAdapter扩展的CursorAdapter {    @燮pressWarnings(德precation)
    公共RecipeAdapter(光标C){
        超(RecipeActivity.this,C);
    }    公共无效bindView(查看排,上下文ARG1,ARG2光标){
        RecipeHolder支架=(RecipeHolder)row.getTag();
        holder.populateFrom(C,DB);    }    公共查看NewView的(上下文为arg0,ARG1光标,ViewGroup中ARG2){
        LayoutInflater吹气= getLayoutInflater();
        查看排= inflater.inflate(R.layout.reciperow,ARG2,FALSE);
        RecipeHolder持有人=新RecipeHolder(行);
        row.setTag(保持器);        返回(行);
    }
静态类RecipeHolder {
    公众的TextView ID = NULL;
    私人TextView的名字= NULL;
    私人TextView的递减= NULL;
    私人TextView的preptime = NULL;
    私人TextView的cooktime = NULL;
    私人TextView的服务= NULL;
    私人TextView的热量= NULL;
    私人TextView的脂肪= NULL;
    私人TextView的最爱= NULL;    RecipeHolder(查看行){
        ID =(TextView中)row.findViewById(R.id.id);
        名称=(TextView中)row.findViewById(R.id.recipe);
        DESC =(TextView中)row.findViewById(R.id.desc);
        preptime =(TextView中)row.findViewById(R.id. preptime);
        cooktime =(TextView中)row.findViewById(R.id.cooktime);
        供应=(TextView中)row.findViewById(R.id.serving);
        卡路里=(TextView中)row.findViewById(R.id.calories);
        脂肪=(TextView中)row.findViewById(R.id.fat);
        最爱=(TextView中)row.findViewById(R.id.fav);
    }
    无效populateFrom(光标C,DBHelper R){
        id.setText(r.getId(C));
        name.setText(r.getRecipe(C));
        name.setTextColor(Color.parseColor(#CCf27c22));
        desc.setText(r.getDesc(C));
        preptime.setText(r.get prepTime(C)+。);
        cooktime.setText(r.getCookTime(C)+分钟);
        serves.setText(r.getServes(C));
        calories.setText(r.getCalories(C));
        fat.setText(r.getFat(C));
        fav.setText(r.getFav(C));

DBHelper类

 公共光标getCursor(){
    SQLiteQueryBuilder的QueryBuilder =新SQLiteQueryBuilder();
    queryBuilder.setTables(DATABASE_TABLE);    的String [] =列新的String [] {KEY_ROWID,食谱,DESC,prePTIME,
            COOKTIME,服务,热量,脂肪,类别,FAV};    光标myCursor = queryBuilder.query(MYDATABASE,列,NULL,NULL,
            NULL,NULL,食谱+ASC);    返回myCursor;
}
公共光标getFilterCursor(CharSequence的约束){
    SQLiteQueryBuilder的QueryBuilder =新SQLiteQueryBuilder();
    queryBuilder.setTables(DATABASE_TABLE);    的String [] =列新的String [] {KEY_ROWID,食谱,DESC,prePTIME,
            COOKTIME,服务,热量,脂肪,类别,FAV};    如果(约束== NULL || constraint.length()== 0){
        //返回完整列表
        返回queryBuilder.query(MYDATABASE,列,NULL,NULL,NULL,
                空,食谱+ASC);
    }其他{
        字符串值=%+ constraint.toString()+%;        返回myDataBase.query(DATABASE_TABLE,列配方样?,
                新的String [] {}值,NULL,NULL,NULL);
    }
}公共字符串的getId(光标C){
               c.moveToFirst();
    返回(c.getString(0));
}公共字符串getRecipe(光标C){
    返回(c.getString(1));
}公共字符串getDesc(光标C){
    返回(c.getString(2));
}公共字符串的get prepTime(光标C){
    返回(c.getString(3));
}公共字符串getCookTime(光标C){
    返回(c.getString(4));
}公共字符串getServes(光标C){
    返回(c.getString(5));
}公共字符串getCalories(光标C){
    返回(c.getString(6));
}公共字符串getFat(光标C){
    返回(c.getString(7));
}公共字符串的getCategory(光标C){
    返回(c.getString(8));
}公共字符串getFav(光标C){
    返回(c.getString(9));
}


解决方案

  @燮pressWarnings(德precation)

坏。你应该摆脱德precation,而不是隐藏了的:)

  startManagingCursor(C);

不要那样做。这可能造成对已经关闭光标重新查询。简单地删除该行。

  adapter.setFilterQueryProvider(新FilterQueryProvider(){
        公共光标runQuery(CharSequence的约束){
            //搜索状态,其名称以字母指定。
            C = db.getFilterCursor(约束);
            返回℃;
        }
    });

不要覆盖你的 C 在这里。刚返回db.getFilterCursor(约束); 这是应该做的。
可能有积极的作用其他的事情

  @燮pressWarnings(德precation)
公共RecipeAdapter(光标C){
    超(RecipeActivity.this,C);
}公共RecipeAdapter(光标C){
    //如果你自己改变光标没有再次查询,也没有要求观察者
    超(RecipeActivity.this,C,0)
}

下一个:

  adapter.notifyDataSetInvalidated();
adapter.changeCursor(NULL);// 改成
adapter.changeCursor(NULL);
adapter.notifyDataSetChanged(); //也许没有这个

据我了解文档 notifyDataSetInvalidated()意味着数据不能有效后(一旦调用这个接口不再有效,不应报告进一步的数据集的变化。),你需要创建一个新的适配器实例。虽然不能肯定。只是做 notifyDataSetChanged()工作正常。它甚至可能是这样做的情况下, adapter.changeCursor()就已经隐含地做更改通知。

不需要

P.S: c.MoveToFirst()。在的CursorAdapter 将光标移动到所需的位置。

I have a ListView in AcitivityA that is populated using a custom SimpleCursorAdapter called RecipeAdapter. The adapter holds data from SQLite

There is a EditText view at the top of the ListView, that filters the listview as the user searches for a recipe. When a user clicks on a item in the filtered ListView, ActivityB starts.

This all works perfectly. However when the user presses the backbutton to resume ActivityB, I get the following error.

java.lang.RuntimeException: Unable to resume activity {ttj.android.quorn/ttj.android.quorn.RecipeActivity}: 
java.lang.IllegalStateException: trying to requery an already closed cursor  android.database.sqlite.SQLiteCursor@418ae5d8

To fix this problem, I modified the onResume() from:

...
c = db.getCursor(); 
adapter.changeCursor(c);

to

....
Cursor cursor = db.getCursor(); 
adapter.changeCursor(cursor);

I then get the following exception. In the Logcat, the problem arises with the getId() method in DBHelper. I have added c.moveToFirst() in this method, but this still doesn't solve the problem.

FATAL EXCEPTION: main
android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 70
at android.database.AbstractCursor.checkPosition(AbstractCursor.java:400)
at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50)
at ttj.android.quorn.DBHelper.getId(DBHelper.java:224)
at ttj.android.quorn.RecipeActivity$RecipeHolder.populateFrom(RecipeActivity.java:650)
at ttj.android.quorn.RecipeActivity$RecipeAdapter.bindView(RecipeActivity.java:572)
at android.support.v4.widget.CursorAdapter.getView(CursorAdapter.java:256)
at android.widget.AbsListView.obtainView(AbsListView.java:2214)
at android.widget.ListView.makeAndAddView(ListView.java:1774)
at android.widget.ListView.fillDown(ListView.java:672)
at android.widget.ListView.fillFromTop(ListView.java:732)
at android.widget.ListView.layoutChildren(ListView.java:1611)
at android.widget.AbsListView.onLayout(AbsListView.java:2044)
at android.view.View.layout(View.java:11418)
at android.view.ViewGroup.layout(ViewGroup.java:4224)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
at android.view.View.layout(View.java:11418)
at android.view.ViewGroup.layout(ViewGroup.java:4224)
at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
at android.view.View.layout(View.java:11418)
at android.view.ViewGroup.layout(ViewGroup.java:4224)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
at android.view.View.layout(View.java:11418)
at android.view.ViewGroup.layout(ViewGroup.java:4224)
at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
at android.view.View.layout(View.java:11418)
at android.view.ViewGroup.layout(ViewGroup.java:4224)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1628)
at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2585)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4507)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)

Can anyone help me with my problem?

Here is my code:

In the onCreate, the cursor populate the ListView using c.getCursor and when the user filters the ListView via the EditText, the c.getFilterCursor is used.

public class RecipeActivity extends SherlockListActivity {

private DBHelper db = null;
private Cursor c = null;
private RecipeAdapter adapter = null;
ListView listContent;   
private EditText filterText = null;

@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
    try {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.filter_list);

        filterText = (EditText) findViewById(R.id.search_box);
        filterText.addTextChangedListener(filterTextWatcher);

        ListView listContent = getListView();

        db = new DBHelper(this);
        db.createDataBase();
        db.openDataBase();

        c = db.getCursor();         

        adapter = new RecipeAdapter(c);

        listContent.setAdapter(adapter);

        adapter.setFilterQueryProvider(new FilterQueryProvider() {
            public Cursor runQuery(CharSequence constraint) {
                // Search for states whose names begin with the specified letters.
                c = db.getFilterCursor(constraint);
                return c;
            }
        });

        startManagingCursor(c);


    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    filterText.removeTextChangedListener(filterTextWatcher);
    db.close();
}


@SuppressWarnings("deprecation")
@Override
protected void onResume() {
    super.onResume();

    Cursor cursor = db.getCursor(); 
    adapter.changeCursor(cursor);

}


@Override
protected void onPause() {
    super.onPause();

    adapter.notifyDataSetInvalidated();
    adapter.changeCursor(null);

}




    private TextWatcher filterTextWatcher = new TextWatcher() {

    public void afterTextChanged(Editable s) {
    }

    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
    }

    public void onTextChanged(CharSequence s, int start, int before,
            int count) {

        adapter.getFilter().filter(s);


    }

};

RecipeAdapter inner class

class RecipeAdapter extends CursorAdapter {

    @SuppressWarnings("deprecation")
    public RecipeAdapter(Cursor c) {
        super(RecipeActivity.this, c);
    }

    public void bindView(View row, Context arg1, Cursor arg2) {
        RecipeHolder holder = (RecipeHolder) row.getTag();
        holder.populateFrom(c, db);

    }

    public View newView(Context arg0, Cursor arg1, ViewGroup arg2) {
        LayoutInflater inflater = getLayoutInflater();
        View row = inflater.inflate(R.layout.reciperow, arg2, false);
        RecipeHolder holder = new RecipeHolder(row);
        row.setTag(holder);

        return (row);
    }


static class RecipeHolder {
    public TextView id = null;
    private TextView name = null;
    private TextView desc = null;
    private TextView preptime = null;
    private TextView cooktime = null;
    private TextView serves = null;
    private TextView calories = null;
    private TextView fat = null;
    private TextView fav = null;

    RecipeHolder(View row) {
        id = (TextView) row.findViewById(R.id.id);
        name = (TextView) row.findViewById(R.id.recipe);
        desc = (TextView) row.findViewById(R.id.desc);
        preptime = (TextView) row.findViewById(R.id.preptime);
        cooktime = (TextView) row.findViewById(R.id.cooktime);
        serves = (TextView) row.findViewById(R.id.serving);
        calories = (TextView) row.findViewById(R.id.calories);
        fat = (TextView) row.findViewById(R.id.fat);
        fav = (TextView) row.findViewById(R.id.fav);
    }


    void populateFrom(Cursor c, DBHelper r) {
        id.setText(r.getId(c));
        name.setText(r.getRecipe(c));
        name.setTextColor(Color.parseColor("#CCf27c22"));
        desc.setText(r.getDesc(c));
        preptime.setText(r.getPrepTime(c) + ". ");
        cooktime.setText(r.getCookTime(c) + " mins");
        serves.setText(r.getServes(c));
        calories.setText(r.getCalories(c));
        fat.setText(r.getFat(c));
        fav.setText(r.getFav(c));

DBHelper class

public Cursor getCursor() {
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
    queryBuilder.setTables(DATABASE_TABLE);

    String[] columns = new String[] { KEY_ROWID, RECIPE, DESC, PREPTIME,
            COOKTIME, SERVES, CALORIES, FAT, CATEGORY, FAV };

    Cursor myCursor = queryBuilder.query(myDataBase, columns, null, null,
            null, null, RECIPE + " ASC");

    return myCursor;
}


public Cursor getFilterCursor(CharSequence constraint) {
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
    queryBuilder.setTables(DATABASE_TABLE);

    String[] columns = new String[] { KEY_ROWID, RECIPE, DESC, PREPTIME,
            COOKTIME, SERVES, CALORIES, FAT, CATEGORY, FAV };

    if (constraint == null || constraint.length() == 0) {
        // Return the full list
        return queryBuilder.query(myDataBase, columns, null, null, null,
                null, RECIPE + " ASC");
    } else {
        String value = "%" + constraint.toString() + "%";

        return myDataBase.query(DATABASE_TABLE, columns, "RECIPE like ? ",
                new String[] { value }, null, null, null);
    }
}

public String getId(Cursor c) {
               c.moveToFirst();
    return (c.getString(0));
}

public String getRecipe(Cursor c) {
    return (c.getString(1));
}

public String getDesc(Cursor c) {
    return (c.getString(2));
}

public String getPrepTime(Cursor c) {
    return (c.getString(3));
}

public String getCookTime(Cursor c) {
    return (c.getString(4));
}

public String getServes(Cursor c) {
    return (c.getString(5));
}

public String getCalories(Cursor c) {
    return (c.getString(6));
}

public String getFat(Cursor c) {
    return (c.getString(7));
}

public String getCategory(Cursor c) {
    return (c.getString(8));
}

public String getFav(Cursor c) {
    return (c.getString(9));
}

解决方案

@SuppressWarnings("deprecation")

Bad. You should get rid of the deprecation instead of hiding that :)

startManagingCursor(c);

Don't do that. That may have caused the requery on the already closed cursor. Simply remove that line.

    adapter.setFilterQueryProvider(new FilterQueryProvider() {
        public Cursor runQuery(CharSequence constraint) {
            // Search for states whose names begin with the specified letters.
            c = db.getFilterCursor(constraint);
            return c;
        }
    });

Don't overwrite your c here. Just return db.getFilterCursor(constraint); is what this should do. Other things that may have a positive effect

@SuppressWarnings("deprecation")
public RecipeAdapter(Cursor c) {
    super(RecipeActivity.this, c);
}

public RecipeAdapter(Cursor c) {
    // no requeries and no observer required if you change the cursor yourself
    super(RecipeActivity.this, c, 0)
}

Next one:

adapter.notifyDataSetInvalidated();
adapter.changeCursor(null);

// change to
adapter.changeCursor(null);
adapter.notifyDataSetChanged(); // maybe without this

As far as I understand the documentation notifyDataSetInvalidated() means that the data can't be valid afterwards ("Once invoked this adapter is no longer valid and should not report further data set changes.") and you need to create a new Adapter instance. Not sure though. Just doing notifyDataSetChanged() works fine. It might even be the case that doing adapter.changeCursor() will already implicitly do the change notification.

P.S.: c.MoveToFirst() is not required. The CursorAdapter will move the cursor to the required position.

这篇关于Android的ListView控件与SimpleCursorAdapter - CursorIndexOutOfBoundsException错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆