2012-08-10 9 views
0

J'ai un ListView dans AcitivityA qui est rempli en utilisant une coutume SimpleCursorAdapter appelé RecipeAdapter. L'adaptateur contient des données de SQLiteAndroid ListView avec SimpleCursorAdapter - Erreur CursorIndexOutOfBoundsException

Il existe une vue EditText en haut du ListView, qui filtre la liste pendant que l'utilisateur recherche une recette. Lorsqu'un utilisateur clique sur un élément dans les champs ListView, ActivityB démarrés.

Tout cela fonctionne parfaitement. Toutefois lorsque l'utilisateur appuie sur le bouton Précédent pour reprendre ActivityB, j'obtiens l'erreur suivante.

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] 

Pour résoudre ce problème, j'ai modifié le onResume() de:

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

à

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

je puis obtenir l'exception suivante. Dans le Logcat, le problème se pose avec la méthode getId() dans DBHelper. J'ai ajouté c.moveToFirst() dans cette méthode, mais cela ne résout toujours pas le problème.

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) 

Quelqu'un peut-il m'aider avec mon problème?

Voici mon code:

Dans le onCreate, le cursor renseignez la ListView en utilisant c.getCursor et lorsque l'utilisateur le filtre ListView via le EditText, le c.getFilterCursor est utilisé.

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 classe interne

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

Répondre

1
@SuppressWarnings("deprecation") 

Mauvais. Vous devriez vous débarrasser de la dépréciation au lieu de le cacher :)

startManagingCursor(c); 

Ne faites pas cela. Cela peut avoir causé la requery sur le curseur déjà fermé. Retirez simplement cette ligne.

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

N'écrasez pas votre c ici. Juste return db.getFilterCursor(constraint); est ce que cela devrait faire. D'autres choses qui peuvent avoir un effet positif

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

suivant:

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

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

Pour autant que je comprends la documentation notifyDataSetInvalidated() signifie que les données ne peuvent être valides après ("Une fois invoqué cet adaptateur n'est plus valide et ne doit pas signaler d'autres modifications de l'ensemble de données. ") et vous devez créer une nouvelle instance de l'adaptateur. Pas sûr cependant. Juste faire notifyDataSetChanged() fonctionne très bien. Il se peut même que adapter.changeCursor() fasse déjà implicitement la notification de modification.

P.S .: c.MoveToFirst() n'est pas requis. Le CursorAdapter va déplacer le curseur à la position requise.

+1

Vous avez fait de moi un homme très heureux! Merci beaucoup pour votre réponse. – tiptopjat

+0

Vous aviez raison, 'adapter.notifyDataSetChanged()' n'était pas nécessaire. – tiptopjat

0

Vous avez renommé votre variable, comme indiqué ici

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

correcte? Mais juste après que vous spécifiez que vous avez essayé

c.moveToFirst() 

Alors peut-être que vous devriez mettre

c = cursor; 

Alors que le reste de votre code fonctionne?

+0

Désolé, 'c.MoveToFirst()' a été ajouté à la classe 'DBHelper', où la variable' Cursor' est 'c'. J'ai renommé Cursor dans la méthode 'onResume()' de ma classe 'MainActivity'. – tiptopjat

Questions connexes