2011-08-29 110 views
7

與其他幾個職位在這裏,我試圖創建一個ListView,包括每行的CheckBox,並使用SQLite數據庫來存儲當前狀態選擇。Android:與CheckBox的ListView,從SQLite數據庫填充不太工作

http://appfulcrum.com/?p=351的例子開始,這個例子並不完全正常,我創建了一個簡單的應用程序來創建數據庫,填充20個項目並顯示列表。

它成功檢索狀態並存儲選擇的狀態。

但是,它不能正確顯示CheckBox狀態,如果我改變它,滾動到列表的另一端,並回滾。例如如果我選擇第一個CheckBox,滾動到底部,然後回到頂部,CheckBox不再設置。這是在Android 2.1三星手機上運行。

如果我回到主屏幕,回到列表中,CheckBox 正確設置,所以數據庫確實已經更新了。

該示例擴展了SimpleCursorAdapter,並且根據表中選擇列的值,getView()根據情況使用true或false調用setChecked()。

以下是所有來源。

我當然很高興被告知, 「咄,這裏是你的問題......」

CustomListViewDB.java

// src/CustomListViewDB.java 
package com.appfulcrum.blog.examples.listviewcustomdb; 

import android.app.ListActivity; 
import android.database.Cursor; 
import android.database.SQLException; 
import android.os.AsyncTask; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.ListView; 
import android.widget.Toast; 

public class CustomListViewDB extends ListActivity { 

    private ListView mainListView = null; 
    CustomSqlCursorAdapter adapter = null; 
    private SqlHelper dbHelper = null; 
    private Cursor currentCursor = null; 

    private ListView listView = null; 

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.simple); 

     if (this.dbHelper == null) { 
      this.dbHelper = new SqlHelper(this); 

     } 

     listView = getListView(); 
     listView.setItemsCanFocus(false); 
     listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); 
     //listView.setClickable(true); 

     Button btnClear = (Button) findViewById(R.id.btnClear); 
     btnClear.setOnClickListener(new OnClickListener() { 

      public void onClick(View v) { 
       Toast.makeText(getApplicationContext(), 
         " You clicked Clear button", Toast.LENGTH_SHORT).show(); 
       ClearDBSelections(); 
      } 
     }); 

     new SelectDataTask().execute(); 

     this.mainListView = getListView(); 

     mainListView.setCacheColorHint(0); 

    } 

    @Override 
    protected void onRestart() { 
     super.onRestart(); 
     new SelectDataTask().execute(); 
    } 

    @Override 
    protected void onPause() { 

     super.onPause(); 
     this.dbHelper.close(); 
    } 

    protected void ClearDBSelections() { 

     this.adapter.ClearSelections(); 

    } 

    private class SelectDataTask extends AsyncTask<Void, Void, String> { 

     protected String doInBackground(Void... params) { 

      try { 

       CustomListViewDB.this.dbHelper.createDatabase(dbHelper.dbSqlite); 
       CustomListViewDB.this.dbHelper.openDataBase(); 

       CustomListViewDB.this.currentCursor = CustomListViewDB.this.dbHelper 
         .getCursor(); 

      } catch (SQLException sqle) { 

       throw sqle; 

      } 
      return null; 
     } 

     // can use UI thread here 
     protected void onPostExecute(final String result) { 

      startManagingCursor(CustomListViewDB.this.currentCursor); 
      int[] listFields = new int[] { R.id.txtTitle }; 
      String[] dbColumns = new String[] { SqlHelper.COLUMN_TITLE }; 

      CustomListViewDB.this.adapter = new CustomSqlCursorAdapter(
        CustomListViewDB.this, R.layout.single_item, 
        CustomListViewDB.this.currentCursor, dbColumns, listFields, 
        CustomListViewDB.this.dbHelper); 
      setListAdapter(CustomListViewDB.this.adapter); 

     } 
    } 

} 

CustomSqlCursorAdapter.java

// src/CustomSqlCursorAdapter.java 

package com.appfulcrum.blog.examples.listviewcustomdb; 

import android.content.ContentValues; 
import android.content.Context; 
import android.database.Cursor; 
import android.database.SQLException; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.CheckBox; 
import android.widget.CompoundButton; 
import android.widget.CompoundButton.OnCheckedChangeListener; 
import android.widget.SimpleCursorAdapter; 
import android.widget.TextView; 

public class CustomSqlCursorAdapter extends SimpleCursorAdapter { 
    private Context mContext; 

    private SqlHelper mDbHelper; 
    private Cursor mCurrentCursor; 

    public CustomSqlCursorAdapter(Context context, int layout, Cursor c, 
      String[] from, int[] to, SqlHelper dbHelper) { 
     super(context, layout, c, from, to); 
     this.mCurrentCursor = c; 
     this.mContext = context; 
     this.mDbHelper = dbHelper; 

    } 

    public View getView(int pos, View inView, ViewGroup parent) { 
     View v = inView; 
     if (v == null) { 
      LayoutInflater inflater = (LayoutInflater) mContext 
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      v = inflater.inflate(R.layout.single_item, null); 
     } 

     if (!this.mCurrentCursor.moveToPosition(pos)) { 
      throw new SQLException("CustomSqlCursorAdapter.getView: Unable to move to position: "+pos); 
     } 

     CheckBox cBox = (CheckBox) v.findViewById(R.id.bcheck); 

     // save the row's _id value in the checkbox's tag for retrieval later 
     cBox.setTag(Integer.valueOf(this.mCurrentCursor.getInt(0))); 

     if (this.mCurrentCursor.getInt(SqlHelper.COLUMN_SELECTED_idx) != 0) { 
      cBox.setChecked(true); 
      Log.w("SqlHelper", "CheckBox true for pos "+pos+", id="+this.mCurrentCursor.getInt(0)); 
     } else { 
      cBox.setChecked(false); 
      Log.w("SqlHelper", "CheckBox false for pos "+pos+", id="+this.mCurrentCursor.getInt(0)); 
     } 
     //cBox.setOnClickListener(this); 
     cBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { 

      @Override 
      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 

       Log.w("SqlHelper", "Selected a CheckBox and in onCheckedChanged: "+isChecked); 

       Integer _id = (Integer) buttonView.getTag(); 
       ContentValues values = new ContentValues(); 
       values.put(SqlHelper.COLUMN_SELECTED, 
         isChecked ? Integer.valueOf(1) : Integer.valueOf(0)); 
       mDbHelper.dbSqlite.beginTransaction(); 
       try { 
        if (mDbHelper.dbSqlite.update(SqlHelper.TABLE_NAME, values, "_id=?", 
          new String[] { Integer.toString(_id) }) != 1) { 
         throw new SQLException("onCheckedChanged failed to update _id="+_id); 
        } 
        mDbHelper.dbSqlite.setTransactionSuccessful(); 
       } finally { 
        mDbHelper.dbSqlite.endTransaction(); 
       } 

       Log.w("SqlHelper", "-- _id="+_id+", isChecked="+isChecked); 
      } 
     }); 

     TextView txtTitle = (TextView) v.findViewById(R.id.txtTitle); 
     txtTitle.setText(this.mCurrentCursor.getString(this.mCurrentCursor 
       .getColumnIndex(SqlHelper.COLUMN_TITLE))); 

     return (v); 
    } 

    public void ClearSelections() { 
     this.mDbHelper.clearSelections(); 
     this.mCurrentCursor.requery(); 

    } 
} 

ListViewWithDBActivity.java

package com.appfulcrum.blog.examples.listviewcustomdb; 

import android.app.Activity; 
import android.os.Bundle; 

public class ListViewWithDBActivity extends Activity { 
    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
    } 
} 

提供SQLHelper

// SqlHelper.java 

package com.appfulcrum.blog.examples.listviewcustomdb; 

import android.content.ContentValues; 
import android.content.Context; 
import android.database.Cursor; 
import android.database.SQLException; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteOpenHelper; 
import android.database.sqlite.SQLiteQueryBuilder; 
import android.database.sqlite.SQLiteStatement; 
import android.util.Log; 

public class SqlHelper extends SQLiteOpenHelper { 
    private static final String DATABASE_PATH = "/data/data/com.appfulcrum.blog.examples.listviewcustomdb/databases/"; 

    public static final String DATABASE_NAME = "TODOList"; 

    public static final String TABLE_NAME = "ToDoItems"; 
    public static final int ToDoItems_VERSION = 1; 

    public static final String COLUMN_ID = "_id";    // 0 
    public static final String COLUMN_TITLE = "title";   // 1 
    public static final String COLUMN_NAME_DESC = "description";// 2 
    public static final String COLUMN_SELECTED = "selected"; // 3 
    public static final int COLUMN_SELECTED_idx = 3; 

    public SQLiteDatabase dbSqlite; 
    private Context mContext; 

    public SqlHelper(Context context) { 
     super(context, DATABASE_NAME, null, 1); 
     mContext = context; 
    } 

    @Override 
    public void onCreate(SQLiteDatabase db) { 
     createDB(db); 
    } 

    @Override 
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
     Log.w("SqlHelper", "Upgrading database from version " + oldVersion 
       + " to " + newVersion + ", which will destroy all old data"); 

     db.execSQL("DROP TABLE IF EXISTS ToDoItems;"); 

     createDB(db); 
    } 

    public void createDatabase(SQLiteDatabase db) { 
     createDB(db); 
    } 

    private void createDB(SQLiteDatabase db) { 
     if (db == null) { 
      db = mContext.openOrCreateDatabase(DATABASE_NAME, 0, null); 
     } 

     db.execSQL("CREATE TABLE IF NOT EXISTS ToDoItems (_id INTEGER PRIMARY KEY, title TEXT, " 
       +" description TEXT, selected INTEGER);"); 
     db.setVersion(ToDoItems_VERSION); 

     // 
     // Generate a few rows for an example 
     // 
     // find out how many rows already exist, and make sure there's some minimum 
     SQLiteStatement s = db.compileStatement("select count(*) from ToDoItems;"); 

     long count = s.simpleQueryForLong(); 
     for (int i = 0; i < 20-count; i++) { 
      db.execSQL("INSERT INTO ToDoItems VALUES(NULL,'Task #"+i+"','Description #"+i+"',0);"); 
     } 
    } 

    public void openDataBase() throws SQLException { 
     String myPath = DATABASE_PATH + DATABASE_NAME; 

     dbSqlite = SQLiteDatabase.openDatabase(myPath, null, 
       SQLiteDatabase.OPEN_READWRITE); 
    } 

    @Override 
    public synchronized void close() { 
     if (dbSqlite != null) 
      dbSqlite.close(); 

     super.close(); 
    } 

    public Cursor getCursor() { 
     SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); 

     queryBuilder.setTables(TABLE_NAME); 

     String[] asColumnsToReturn = new String[] { COLUMN_ID, COLUMN_TITLE, 
       COLUMN_NAME_DESC, COLUMN_SELECTED }; 

     Cursor mCursor = queryBuilder.query(dbSqlite, asColumnsToReturn, null, 
       null, null, null, COLUMN_ID+" ASC"); 

     return mCursor; 
    } 

    public void clearSelections() { 
     ContentValues values = new ContentValues(); 
     values.put(COLUMN_SELECTED, 0); 
     this.dbSqlite.update(SqlHelper.TABLE_NAME, values, null, null); 
    } 
} 

Start.java

//src/Start.java 
package com.appfulcrum.blog.examples.listviewcustomdb; 

import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.Toast; 

public class Start extends Activity { 
    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     Button btnSimple = (Button) findViewById(R.id.btnSimple); 
     btnSimple.setOnClickListener(new OnClickListener() { 

      public void onClick(View v) { 

       Toast.makeText(getApplicationContext(), 
         " You clicked ListView From DB button", Toast.LENGTH_SHORT).show(); 

       Intent intent = new Intent(v.getContext(), CustomListViewDB.class); 
       startActivityForResult(intent, 0); 
      } 
     }); 

    } 
} 

佈局/ main.xml中

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/buttonlayout" android:orientation="vertical" 
    android:layout_width="fill_parent" android:layout_height="fill_parent" 
    android:gravity="left|top" android:paddingTop="2dp" 
    android:paddingBottom="2dp"> 

    <TextView android:id="@+id/txtTest" android:layout_width="fill_parent" 
     android:layout_height="wrap_content" android:textStyle="bold" 
     android:text="@string/app_name" android:textSize="15sp" 
     android:textColor="#FF0000" android:gravity="center_vertical" 
     android:paddingLeft="5dp"> 
    </TextView> 

    <Button android:id="@+id/btnSimple" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:textSize="15sp" 
     android:text="Listview from DB" 
     android:textColor="#000000" 
     > 
    </Button> 

</LinearLayout> 

佈局/ simple.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" android:layout_width="fill_parent" 
    android:layout_height="fill_parent"> 

    <LinearLayout android:id="@+id/buttonlayout" 
     android:orientation="horizontal" android:layout_width="fill_parent" 
     android:layout_height="wrap_content" android:height="32dp" 
     android:gravity="left|top" android:paddingTop="2dp" 
     android:paddingBottom="2dp"> 

     <LinearLayout android:id="@+id/buttonlayout2" 
      android:orientation="horizontal" android:layout_height="wrap_content" 
      android:gravity="left|center_vertical" android:layout_width="wrap_content" 
      android:layout_gravity="left|center_vertical"> 

      <TextView android:id="@+id/txtTest" 
       android:layout_width="fill_parent" 
       android:layout_height="fill_parent" android:textStyle="bold" 
       android:text="@string/list_header" android:textSize="15sp" 
       android:gravity="center_vertical" android:paddingLeft="5dp"> 
      </TextView> 

      <Button android:id="@+id/btnClear" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" android:text="Clear" 
       android:textSize="15sp" android:layout_marginLeft="10px" 
       android:layout_marginRight="10px" 
       android:layout_marginBottom="2px" 
       android:layout_marginTop="2px" android:height="15dp" 
       android:width="70dp"></Button> 
     </LinearLayout> 
    </LinearLayout> 

    <TableLayout android:id="@+id/TableLayout01" 
     android:layout_width="fill_parent" android:layout_height="fill_parent" 
     android:stretchColumns="*"> 
     <TableRow> 
      <ListView android:id="@android:id/list" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content"></ListView> 
     </TableRow> 

    </TableLayout> 

</LinearLayout> 

佈局/ single_item.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" android:layout_height="wrap_content" 
    android:orientation="horizontal" android:gravity="center_vertical"> 

    <CheckBox android:id="@+id/bcheck" 
       android:layout_width="wrap_content" 
       android:layout_height="fill_parent" /> 

     <TextView android:id="@+id/txtTitle" 
      android:layout_width="wrap_content" android:gravity="left|center_vertical" 
      android:layout_height="?android:attr/listPreferredItemHeight" 
      android:layout_alignParentLeft="true" 
      android:textSize="20sp" android:text="Test" 
      android:textStyle="bold" android:paddingLeft="5dp" 
      android:paddingRight="2dp" android:focusable="false" 
      android:focusableInTouchMode="false"></TextView> 
     <LinearLayout android:layout_width="fill_parent" 
      android:layout_height="wrap_content" android:orientation="horizontal" 
      android:gravity="right|center_vertical"> 
     </LinearLayout> 

</LinearLayout> 

值/串。XML

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <string name="hello">Hello World, ListViewWithDBActivity!</string> 
    <string name="app_name">ListViewWithDB</string> 
    <string name="list_header">List Headers</string> 
</resources> 

AndroidManifest.xml中

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    android:versionCode="1" android:versionName="1.0"  
    package="com.appfulcrum.blog.examples.listviewcustomdb"> 

    <application android:icon="@drawable/icon" 
     android:label="@string/app_name" 
     android:theme="@android:style/Theme.NoTitleBar"> 
     > 
     <activity android:name=".Start" android:label="@string/app_name"> 
      <intent-filter> 
       <action android:name="android.intent.action.MAIN" /> 
       <category android:name="android.intent.category.LAUNCHER" /> 
      </intent-filter> 
     </activity> 
     <activity android:name=".CustomListViewDB"></activity> 
    </application> 

    <uses-sdk android:minSdkVersion="7" /> <!-- android 1.6 --> 
</manifest> 

如果你想建立,拋出一些任意的icon.png到繪製。

在此先感謝。

+4

TL; DR ...真的,儘量更簡潔。 –

+1

像這樣的其他問題更簡潔,但仍然讓人感到困惑。我想根據需要提供儘可能多的信息。正如我在其他答覆中所指出的,另一個網站爲確切問題提供瞭解決方案。乾杯。值得一試的 – user877139

回答

2

ListView中的視圖被回收,這聽起來像是一個問題。您可能需要使您的onCheckedChangedListener無效,以便當您執行setChecked()時,它不會無意中調用上一個偵聽器。可能還有其他回收的影響,請記住這一點。

所以嘗試:

cBox.setOnCheckedChangeListener(null); 
... 
cBox.setChecked(); 
... 
cBox.setOnCheckedChangeListner(<real listener); 
+0

。試過。沒有改變行爲。通過調試器確認設備上有新代碼。它感覺像它緩存了一行沒有被刷新的數據庫,直到我離開列表。但這就是爲什麼我添加了所有交易代碼。 – user877139

+0

工作在我的情況 – defhlt