2012-08-10 86 views
0

我有一個ListViewAcitivityA,它使用自定義SimpleCursorAdapter填充,稱爲RecipeAdapter。適配器保存來自SQLite的數據帶有SimpleCursorAdapter的Android ListView - CursorIndexOutOfBoundsException錯誤

ListView的頂部有一個EditText視圖,用於在用戶搜索配方時過濾列表視圖。當用戶點擊已過濾的ListView中的項目時,ActivityB開始。

這一切都完美。但是,當用戶按下後退按鈕以恢復ActivityB時,出現以下錯誤。

java.lang.RuntimeException: Unable to resume activity {ttj.android.quorn/ttj.android.quorn.RecipeActivity}: 
java.lang.IllegalStateException: trying to requery an already closed cursor [email protected] 

爲了解決這個問題,我從修改onResume()

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

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

然後我得到下面的異常。在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,該cursor使用c.getCursor填充ListView當用戶通過過濾的EditTextListView,使用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 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)); 
} 

回答

1
@SuppressWarnings("deprecation") 

不好。你應該擺脫棄用而不是隱藏:) :)

startManagingCursor(c); 

不這樣做。這可能導致已經關閉的光標的重新查詢。只需刪除該行。

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。只是return db.getFilterCursor(constraint);是這個應該做的。 可能有積極的作用其他的事情

@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()意味着數據不能有效之後(「一旦調用此適配器不再有效,並且不應報告更多數據集更改「),並且您需要創建一個新的Adapter實例。但不知道。只是做notifyDataSetChanged()工作正常。甚至可能是這樣做的:adapter.changeCursor()已經隱含地執行了更改通知。

P.S .: c.MoveToFirst()不是必需的。 CursorAdapter將光標移動到所需的位置。

+1

你讓我變得很幸福!非常感謝您提供SUPERB答案。 – tiptopjat 2012-08-10 22:38:15

+0

你說得對,'adapter.notifyDataSetChanged()'不需要。 – tiptopjat 2012-08-10 22:39:23

0

你改名爲您的變量,這裏

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

正確的指示?但之後,你指定你試過

c.moveToFirst() 

因此,也許你應該設置

c = cursor; 

使你的代碼的其他作品?

+0

對不起,'c.MoveToFirst()'已被添加到'DBHelper'類,其中'Cursor'變量是'C'。我在我的MainActivity類的'onResume()'方法中重命名了Cursor。 – tiptopjat 2012-08-10 21:23:30

相關問題