2011-08-29 9 views
7

Podobnie jak w przypadku kilku innych postów tutaj, próbuję utworzyć ListView, który zawiera CheckBox dla każdego wiersza i użyć bazy danych SQLite do przechowywania bieżącego stanu wybór.Android: ListView z CheckBox, wypełnione z bazy danych SQLite niezupełnie działające

Zaczynając od przykładu pod adresem http://appfulcrum.com/?p=351, który nie działał tak, jak jest, utworzyłem prostą aplikację, która tworzy bazę danych, zapełnia ją 20 pozycjami i wyświetla listę.

Pomyślnie pobiera stan i przechowuje stan zaznaczenia.

ALE, nie pokazuje poprawnie stanu CheckBox, jeśli go zmieniam, przewiń do drugiego końca listy i przewiń w tył. na przykład jeśli wybiorę pierwszy CheckBox, przewiń do dołu i wróć na górę, CheckBox nie jest już ustawiony. Jest to uruchamiane na telefonie Samsung Android 2.1.

Jeśli wrócę do ekranu głównego, powrócę do listy, to CheckBox jest poprawnie ustawiony, więc baza danych rzeczywiście została zaktualizowana.

Przykład rozszerza SimpleCursorAdapter, a metoda getView() wywołuje metodę setChecked() z wartością true lub false w zależności od potrzeb, w zależności od wartości kolumny wyboru w tabeli.

Poniżej znajdują się wszystkie źródła.

Ja na pewno docenią powiedział, "Duh, tu jest twój problem ..."

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

    } 
} 

układ/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> 

układ/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> 

układ/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> 

wartości/sznurki.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> 

Jeśli chcesz zbudować, rzucić nieco arbitralne icon.png do rozciągliwej.

Z góry dziękuję.

+4

TL; DR ... Naprawdę, staraj się być bardziej zwięzły. –

+1

Inne pytania, takie jak te, były bardziej zwięzłe, ale nadal sprawiały, że ludzie byli zdezorientowani. Chciałem dostarczyć tyle informacji, ile potrzeba. Jak wspomniano w mojej dodatkowej odpowiedzi, inna strona zapewniała rozwiązanie dla dokładnego problemu. Twoje zdrowie. – user877139

Odpowiedz

2

Widoki w ListView są przetwarzane i to brzmi jak problem z tym. Prawdopodobnie musisz unieważnić swój onCheckedChangedListener, aby po wykonaniu setChecked() nie wywoływał nieumyślnie poprzedniego słuchacza. Mogą istnieć inne konsekwencje recyklingu, więc miej to na uwadze.

Więc spróbuj:

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

Warto spróbować. Wypróbowany. Nie zmieniło to zachowania. Upewniłem się, że w debugerze na urządzeniu był nowy kod. To sprawia wrażenie, jakby buforował wiersz bazy danych, który nie jest przepłukiwany, dopóki nie opuszczę listy. Ale właśnie dlatego dodałem cały kod transakcji. – user877139

+0

Pracowałem w moim przypadku – defhlt

Powiązane problemy