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