Java Android ListView带有SimpleCursorAdapter-CursorIndexOutOfBoundsException错误

Java Android ListView带有SimpleCursorAdapter-CursorIndexOutOfBoundsException错误,java,android,sqlite,listview,cursor,Java,Android,Sqlite,Listview,Cursor,我在AcitivityA中有一个ListView,它是使用名为RecipeAdapter的自定义SimpleCursorAdapter填充的。适配器保存来自SQLite的数据 列表视图顶部有一个EditText视图,当用户搜索配方时,该视图会过滤列表视图。当用户单击筛选的列表视图中的项目时,活动b启动 这一切都很完美。但是,当用户按下backbutton以继续活动b时,我得到以下错误 java.lang.RuntimeException: Unable to resume activity {t

我在
AcitivityA
中有一个
ListView
,它是使用名为
RecipeAdapter
的自定义
SimpleCursorAdapter
填充的。适配器保存来自SQLite的数据

列表视图
顶部有一个
EditText
视图,当用户搜索配方时,该视图会过滤列表视图。当用户单击筛选的
列表视图中的项目时,
活动b
启动

这一切都很完美。但是,当用户按下backbutton以继续
活动b
时,我得到以下错误

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
为了解决这个问题,我修改了
onResume()

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

然后我得到以下异常。在Logcat中,问题出现在
DBHelper
中的
getId()
方法中。我在这个方法中添加了
c.moveToFirst()
,但这仍然不能解决问题

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)
有人能帮我解决我的问题吗

这是我的密码:

onCreate
中,使用
c.getCursor
填充
列表视图
,当用户通过
EditText
过滤
列表视图时,使用
c.getFilterCursor

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
内部类

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类

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

您重命名了变量,如下所示

....
Cursor cursor = db.getCursor(); 
adapter.changeCursor(cursor);
对吗?但就在那之后,你指定你已经试过了

c.moveToFirst()
所以也许你应该设定

c = cursor;
这样你剩下的代码就可以工作了

@SuppressWarnings("deprecation")
糟糕。您应该摆脱这种不赞成,而不是隐藏:)

不要那样做。这可能导致重新查询已关闭的光标。只需删除该行

    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;
        }
    });
不要在此处覆盖您的
c
。只需
返回db.getFilterCursor(约束)是它应该做的。
其他可能产生积极影响的事情

@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)
}
下一个:

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

// change to
adapter.changeCursor(null);
adapter.notifyDataSetChanged(); // maybe without this
据我所知,文档
notifyDataSetInvalidated()
意味着数据在之后将无法生效(“一旦调用,此适配器将不再有效,并且不应报告进一步的数据集更改”)。您需要创建一个新的适配器实例。不过我不确定。只需执行
notifyDataSetChanged()
即可。甚至可能在这种情况下,执行
adapter.changeCursor()
已经隐式执行更改通知


注意:
c.不需要MoveToFirst()
CursorAdapter
将光标移动到所需的位置。

对不起,
c.MoveToFirst()
已添加到
DBHelper
类中,其中
cursor
变量为
c
。我在我的
MainActivity
类的
onResume()
方法中重命名了游标。你让我成为了一个非常快乐的人!非常感谢您的精彩回答。您是对的,
adapter.notifyDataSetChanged()
不需要。
@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)
}
adapter.notifyDataSetInvalidated();
adapter.changeCursor(null);

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