2015-09-27 62 views
0

因此,我的應用程序中存儲了一個SQLite數據庫,用於存儲最喜歡的項目(壁紙)。我開始學習數據庫管理,所以這對我來說很新鮮。但是,我設法使基本數據庫工作,包括添加,刪除和查詢...除了查詢需要一些工作。在Android SQLite數據庫中刪除第一項後光標大小爲零

基本上,用戶打開一個包含圖像的活動。他們打開一個菜單項,將當前圖像添加到數據庫(使用字符串值,因爲圖像是從互聯網上檢索的)。

接下來,我有一個特殊的片段列出了我的最愛。當你長按列表中的一個項目時,它將從數據庫中刪除。

這是我的問題:刪除項目工作正常,除非我刪除數據庫中的第一項。出於某種原因,導致getFavorite()中的光標變爲零,我得到一個異常。

看到我的代碼在下面...很多種類,但我希望有人能幫助我!

代碼

SQLite數據庫助手

public class FavoritesHandler extends SQLiteOpenHelper { 

    // All Static variables 
    // Database Version 
    private static final int DATABASE_VERSION = 1; 

    // Database Name 
    private static final String DATABASE_NAME = "favoritesManager"; 

    // Contacts table name 
    private static final String TABLE_FAVORITES = "favoriteWallpapers"; 
    // Contacts Table Columns names 
    private static final String KEY_ID = "id"; 
    private static final String KEY_URL = "url"; 
    private static final String KEY_FILENAME = "file_name"; 

    private static final String[] COLUMNS = {KEY_ID, KEY_URL, KEY_FILENAME}; 

    public FavoritesHandler(Context context) { 
     super(context, DATABASE_NAME, null, DATABASE_VERSION); 
    } 

    @Override 
    public void onCreate(SQLiteDatabase sqLiteDatabase) { 
     String CREATE_CONTACTS_TABLE = "CREATE TABLE " + TABLE_FAVORITES + "(" 
       + KEY_ID + " INTEGER PRIMARY KEY, " 
       + KEY_URL + " TEXT, " 
       + KEY_FILENAME + " TEXT" + ");"; 
     sqLiteDatabase.execSQL(CREATE_CONTACTS_TABLE); 
    } 

    @Override 
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { 
     sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES + ";"); 
     onCreate(sqLiteDatabase); 
    } 

    public void addFavorite(Favorite favorite) { 
     SQLiteDatabase db = this.getWritableDatabase(); 

     ContentValues values = new ContentValues(); 
     values.put(KEY_URL, favorite.getURL()); // Favorite URL 
     values.put(KEY_FILENAME, favorite.getFilename()); // Favorite Filename 

     // Inserting Row 
     db.insert(TABLE_FAVORITES, null, values); 
     db.close(); // Closing database connection 
    } 

    public Favorite getFavorite(int id) { 
     SQLiteDatabase db = this.getReadableDatabase(); 

     Cursor cursor = db.query(TABLE_FAVORITES, new String[]{KEY_ID, 
         KEY_URL, KEY_FILENAME}, KEY_ID + "=?", 
       new String[]{String.valueOf(id)}, null, null, null, null); 
     if (cursor != null) 
      cursor.moveToFirst(); 

     // line 74 
     return new Favorite(Integer.parseInt(cursor.getString(0)), 
       cursor.getString(1), cursor.getString(2)); 

    } 

    public ArrayList<Favorite> getAllFavorites() { 
     ArrayList<Favorite> favoriteList = new ArrayList<>(); 
     // Select All Query 
     String selectQuery = "SELECT * FROM " + TABLE_FAVORITES; 

     SQLiteDatabase db = this.getWritableDatabase(); 
     Cursor cursor = db.rawQuery(selectQuery, null); 

     // looping through all rows and adding to list 
     if (cursor.moveToFirst()) { 
      do { 
       Favorite favorite = new Favorite(); 
       favorite.setID(cursor.getInt(0)); 
       favorite.setURL(cursor.getString(1)); 
       favorite.setFilename(cursor.getString(2)); 
       favoriteList.add(favorite); 
      } while (cursor.moveToNext()); 
     } 

     // return contact list 
     return favoriteList; 
    } 

    public int getFavoritesCount() { 
     SQLiteDatabase db = this.getReadableDatabase(); 
     String countQuery = "SELECT * FROM " + TABLE_FAVORITES + ";"; 
     Cursor cursor = db.rawQuery(countQuery, null); 
     int count = cursor.getCount(); 
     cursor.close(); 

     // return count 
     return count; 
    } 

    public void deleteFavorite(Favorite favorite) { 
     SQLiteDatabase db = this.getWritableDatabase(); 
     db.delete(TABLE_FAVORITES, " id = ?", 
       new String[]{String.valueOf(favorite.getID())}); 
     db.close(); 
    } 
} 

喜愛職業

public class Favorite { 

    int _ID; 
    String _URL; 
    String _FILENAME; 

    public Favorite() { 
    } 

    public Favorite(int ID, String URL, String Filename) { 
     this._ID = ID; 
     this._URL = URL; 
     this._FILENAME = Filename; 
    } 

    public Favorite(String URL, String Filename) { 
     this._URL = URL; 
     this._FILENAME = Filename; 
    } 

    public int getID() { 
     return _ID; 
    } 

    public void setID(int _ID) { 
     this._ID = _ID; 
    } 

    public String getURL() { 
     return _URL; 
    } 

    public void setURL(String _URL) { 
     this._URL = _URL; 
    } 

    public String getFilename() { 
     return _FILENAME; 
    } 

    public void setFilename(String _FILENAME) { 
     this._FILENAME = _FILENAME; 
    } 
} 

自定義圖像適配器在收藏片段

private class ImageAdapter extends BaseAdapter { 

    @Override 
    public int getCount() { 
     FavoritesHandler favoritesHandler = new FavoritesHandler(getActivity().getBaseContext()); 
     return favoritesHandler.getFavoritesCount(); 
    } 

    @Override 
    public Object getItem(int position) { 
     return null; 
    } 

    @Override 
    public long getItemId(int position) { 
     return position; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     View view = convertView; 
     final ViewHolder gridViewImageHolder; 
     if (convertView == null) { 
      view = getActivity().getLayoutInflater().inflate(R.layout.util_grid_item_image, parent, false); 
      gridViewImageHolder = new ViewHolder(); 
      gridViewImageHolder.imageView = (ImageView) view.findViewById(R.id.util_wallpaper_image); 
      view.setTag(gridViewImageHolder); 
     } else { 
      gridViewImageHolder = (ViewHolder) view.getTag(); 
     } 

     FavoritesHandler db = new FavoritesHandler(getActivity()); 

     List<Favorite> favorites = db.getAllFavorites(); 

     for (Favorite favorite : favorites) { 
      favorite = db.getFavorite(position + 1); 

      // line 136 
      imageLoader.displayImage(favorite.getURL() + favorite.getFilename(), 
        gridViewImageHolder.imageView, 
        ImageLoaderUtil.setupOptions()); 
     } 

     return view; 
    } 
} 

異常後刪除在顯示的列表中第一個項目

09-27 02:04:38.641 30338-30338/? E/libEGL: call to OpenGL ES API with no current context (logged once per thread) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime: FATAL EXCEPTION: main 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime: Process: com.hidden.hidden, PID: 30338 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime: android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.database.AbstractCursor.checkPosition(AbstractCursor.java:460) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at com.hidden.hidden.FavoritesHandler.getFavorite(FavoritesHandler.java:74) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at com.hidden.hidden.fragments.FavoritesFragment$ImageAdapter.getView(FavoritesFragment.java:136) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.AbsListView.obtainView(AbsListView.java:2346) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.GridView.onMeasure(GridView.java:1065) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.View.measure(View.java:18788) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.View.measure(View.java:18788) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.View.measure(View.java:18788) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.View.measure(View.java:18788) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:940) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.View.measure(View.java:18788) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.support.v7.internal.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:124) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.View.measure(View.java:18788) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.View.measure(View.java:18788) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.View.measure(View.java:18788) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.LinearLayout.measureVertical(LinearLayout.java:748) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.LinearLayout.onMeasure(LinearLayout.java:630) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.View.measure(View.java:18788) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2643) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.View.measure(View.java:18788) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2100) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1216) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1452) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.Choreographer.doCallbacks(Choreographer.java:670) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.Choreographer.doFrame(Choreographer.java:606) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.os.Handler.handleCallback(Handler.java:739) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.os.Handler.dispatchMessage(Handler.java:95) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.os.Looper.loop(Looper.java:148) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at android.app.ActivityThread.main(ActivityThread.java:5417) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at java.lang.reflect.Method.invoke(Native Method) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
09-27 02:04:40.195 30338-30338/com.hidden.hidden E/AndroidRuntime:  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

我一直在努力嘗試,現在找到這個約3天的解決方案...我認爲這需要一套的新眼睛。

編輯

添加行號。

+0

whats在FavoritesHandler.java:74你可以請編輯和刪除所有的基本代碼和堆棧跟蹤的基本位 – e4c5

+0

@ e4c5 - 我已更新我的問題,標有行號。 –

回答

0

嗨,我看到您的發佈代碼和例外。看起來問題是由於索引超出索引。在你的適配器中,問題是適配器的getCount()方法和getView(..)方法內部請參閱下面我已更新適配器代碼。

private class ImageAdapter extends BaseAdapter { 

    private List<Favorite> favorites = new ArrayList(); 
    private FavoritesHandler db; 

    public ImageAdapter(){ 
     db = new FavoritesHandler(getActivity()); 
     favorites.addAll(db.getAllFavorites()); 
    } 

    @Override 
    public int getCount() { 
     return favorites.size(); 
    } 

    @Override 
    public long getItemId(int position) { 
     return position; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     View view = convertView; 
     final ViewHolder gridViewImageHolder; 
     if (convertView == null) { 
      gridViewImageHolder = new ViewHolder(); 
      view = getActivity().getLayoutInflater().inflate(R.layout.util_grid_item_image, parent, false); 
      gridViewImageHolder.imageView = (ImageView) view.findViewById(R.id.util_wallpaper_image); 
      view.setTag(gridViewImageHolder); 
     } else { 
      gridViewImageHolder = (ViewHolder) view.getTag(); 
     } 

     Favorite favorite = favorites.gePosition(position); 
    imageLoader.displayImage(favorite.getURL() + favorite.getFilename(),gridViewImageHolder.imageView,ImageLoaderUtil.setupOptions()); 

     return view; 
    } 
} 

讓我知道你是否有任何問題。謝謝。

+0

完美。謝謝。 –

+0

不客氣:_) –

1

我認爲這個錯誤是由你試圖獲取你剛纔刪除的不存在的元素引起的。

favorite = db.getFavorite(position + 1); 

當位置爲0時,這將查詢刪除的項目。

if (cursor != null) 
     cursor.moveToFirst(); 

這會使您的代碼失敗,因爲您正在查詢已刪除的項目。這會返回一個空的遊標,它不是null,但嘗試移動到第一個結果行會導致IndexOutOfBounds異常。

您所犯的一般錯誤是您將您的物品清單位置與他們的身份證綁定,這是一種不好的做法。您爲用戶呈現項目的方式應該獨立於其數據庫表示。

如果我可以爲你提供一些更多的指針:

  • 適配器實現相當無效的。您正在多次訪問數據庫 - 一次查找計數,一次查找您爲用戶呈現的每個項目。如果您使用CursorAdapter或Loader,可以避免這種情況,它會爲您提取收藏夾列表並創建某種自定義ArrayAdapter實例。
  • 您絕對應該考慮將DB訪問從主線程移出,以確保性能安全。
+0

感謝您的強烈批評!我會考慮讓這更有效。 –

+1

我建議您還查看'ContentProviders'以進一步瞭解如何在Android中使用SQLite。這些例子寫得很好,它們強調最佳實踐。希望這可以幫助你。 –

1

在這裏做幾個要點。

首先,一個問題在於你的getFavorite()方法的構建。

此方法採用int參數,該參數指定要從遊標檢索的行的KEY_ID。因此,每次調用getFavorite()時,它只返回一行數據庫。當您在getView()中調用此方法時,您通過position + 1作爲它的參數。由於此行不再存在於您的數據庫中(因爲_id列自動增量,並且第一行是位置(0 + 1)),因此您的Cursor將返回爲空,因此如果嘗試訪問它,則會出現錯誤。

要解決該問題,請不要在getView()中撥打db.getAllFavorites()db.getFavorite()。從性能的角度來看,除了從這個角度來持續運行查詢,這是一個非常糟糕的想法,KEY_IDCursor的行號並不總是完全一致。例如,假設有一個Cursor有三項:

_id name 
1  Cheddar 
2  Parmesan 
3  Brie 

如果你的1KEY_ID值刪除行,然後調用你的光標會說,這有兩個條目getCount()。但是,如果您嘗試query()您的數據庫與KEY_ID1,那麼您將返回一個空的遊標(無結果)。你改爲訪問的是行號之一的光標,而不是_id

要做到這一點,你需要query()getView()外數據庫,存儲返回Cursor到一個變量,要麼使用直接填充列表項,或將其寫入到某種形式的List和使用改爲List。您的getAllFavorites()方法似乎是這樣做的,所以您應該使用從此返回的ArrayList來填充您的列表。

一旦您對用於填充適配器的數據進行了任何更改(例如,刪除之後),一定要在適配器上調用notifyDataSetChanged()

我已經編寫了一個couple of blog posts的源代碼和一個示例應用程序,主題爲Android中的SQLite數據庫,如果您有興趣,可以詳細瞭解SQLite數據庫。

值得一提的最後一點是,您可以改善您的方法的性能。 Android中的數據庫操作速度很慢,因此您希望儘可能少地調用直接與它們交互的方法,如query()。理想的情況是數據庫被查詢一次,然後所有進一步的操作都在返回的cursor上。如果數據庫中的數據發生了重大變化,則再次查詢一次並致電notifyDataSetChanged()。目前,您每次調用getView()時都會調用getAllFavorites(),這會查詢整個數據庫(實際上只要您的列表滾動)。然後,即使getAllFavorites()返回您需要多次的所有信息,通過調用getFavorite()可以不必要地重寫每個條目。另外,大的query()調用應該在主線程之外發生,否則它們可以將UI鎖定幾秒鐘。 AsyncTasks對此很有幫助。

+0

感謝您的詳細解答。我做了上面提到的Bhavdip和固定的東西。 –