Android: SimpleCursorAdapter usage

2019-04-15 17:00发布

问题:

I have ONE annoying problem with SimpleCursorAdapter. My programm has list view and ListActivity. Each row has it's own layout:

<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content" android:layout_width="fill_parent"
android:orientation="horizontal" android:weightSum="1.0">
<TableRow>
    <TextView android:id="@+id/task_time"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:textSize="24sp" android:text="Time">
    </TextView>
    <LinearLayout android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent">
        <TextView android:id="@+id/task_name"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:textSize="20sp" android:text="Name">
        </TextView>
        <TextView android:id="@+id/task_categoty"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="Category" android:textSize="12sp">
        </TextView>
    </LinearLayout>
    <TextView android:id="@+id/task_state"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="State" android:textSize="12sp">
    </TextView>
    <CheckBox android:id="@+id/task_enabled"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:focusable="false">
    </CheckBox>

</TableRow>

Tasks are stored in SQLite database. I have DAO object (singleton) to access the database. TaskDao:

    public void updateEnabled(int id, boolean enabled){
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    ContentValues cv = new ContentValues();
    cv.put(ENABLED_COLUMN, enabled==true?1:0);
    Log.i(TAG, "update to " + cv.get(ENABLED_COLUMN) );
    try{
        db.beginTransaction();
        db.update(TASK_TABLE, cv, ID_COLUMN+"=?", new String[]{id+""});
        db.setTransactionSuccessful();
    } catch (SQLException e) {
        Log.i(TAG, "edit task failed!");
    } finally {
        db.endTransaction();
        if (db != null)
            db.close();
    }
}

and the Cursor method for ListActivity:

    public Cursor getTasks(){
    SQLiteDatabase db = dbHelper.getReadableDatabase();
    return db.query(TASK_TABLE, COLUMNS, null, null, null, null, NAME_COLUMN);
}

I extended SimpleCursorAdapter (TaskDbAdapter) like this:

    @Override
public View getView(int position, View convertView, ViewGroup parent) {
    if(convertView==null){
        convertView = inflater.inflate(R.layout.task_list_row, null);
    }
    Cursor c = getCursor();
    c.moveToPosition(position);
    Log.i(TAG, "getView " + position + " = " + c.getInt(enabledIdx));
    enabled.setTag(c.getInt(c.getColumnIndex(BaseColumns._ID)));
    enabled.setChecked(c.getInt(enabledIdx)>0?true:false);
    enabled.setOnClickListener(this);
    return convertView;
}
@Override
public void onClick(View v) {
    CheckBox box = (CheckBox) v;
    Integer id = (Integer)box.getTag();
    TaskDao.getInstance(context).updateEnabled(id.intValue(), box.isChecked());
}

And at last I use all the above stuff in my main ListActivity

    private void refreshList(){
    c = TaskDao.getInstance(this).getTasks();
    startManagingCursor(c);
    adapter = new TaskDbAdapter(this, R.layout.task_list_row, c, new String[]{TaskDao.ENABLED_COLUMN}, new int[]{R.id.task_enabled});
    setListAdapter(adapter);
}
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.task);
    getListView().setItemsCanFocus(false);
    getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    getListView().setVerticalScrollBarEnabled(true);
    registerForContextMenu(getListView());
    getListView().setOnCreateContextMenuListener(this);
    refreshList();
}


@Override
protected void onResume() {
    super.onResume();
    refreshList();
}
@Override
protected void onPause() {
    super.onPause();

}

Everything works fine. But CheckBoxes loose their states. For instance I check my first column and scroll the list down. In my trace before press I have:

getView 0 = 0
getView 2 = 0
getView 3 = 0

then

uptate to 1

and then (when I scroll up to the first element)

getView 0 = 0
getView 2 = 0
getView 3 = 0

I tried to make getCursor().requery(); in my TaskDbAdapter onClick method. But then I saw no items in the list! And exception because of cursor management(connection was closed by android). When I write startManagingCursor(c); in refreshList() method then check and uncheck methods don't work. Please, Help!

回答1:

I didn't read all your source so my suggestion may be totally wrong, but I will give a try.

Take a look at the documentation of BaseAdapter class.

public void notifyDataSetChanged () 

may do the work.

You also can register Observer for this...

public void registerDataSetObserver (DataSetObserver observer)


回答2:

I struggled with this as well. I ended up storing all checked boxes in the db as either 0 or 1. Then I check their state from the database to determine if they are marked or not.

public class DetailCursorAdapter extends SimpleCursorAdapter {

private Cursor c;
private Context context;    

public DetailCursorAdapter(Context context, int layout, Cursor c,
        String[] from, int[] to) {
    super(context, layout, c, from, to);
    this.c = c;
        this.context = context;

}

public View getView(int pos, View inView, ViewGroup parent) {
    View v = inView;
    if (v == null) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = inflater.inflate(R.layout.check_list, null);
    }
    Log.i("pos = ..................", "pos = "+pos);
    this.c.moveToPosition(pos); 
    //this.c.moveToPosition(this.c.getInt(this.c.getColumnIndex("_id")));   
    CheckBox cBox = (CheckBox) v.findViewById(R.id.bcheck);
    cBox.setTag(this.c.getInt(this.c.getColumnIndex("_id")));

    /*
     * when reloading the list, check for chkd status, this is broken.  Need to query db directly.
     */
    EventDbAdapter mDbHelper = new EventDbAdapter(context);
    mDbHelper.open(); 

    int idTag = (Integer) cBox.getTag();                
    int checked = mDbHelper.selectChk(idTag);
    mDbHelper.close();
    Log.i("results from selectChk.....................", ""+checked);
    if (checked == 1) {
        cBox.setChecked(true);          
    } else {
        cBox.setChecked(false);
    }

    /*
     * Populate the list
     */     
    TextView txtdateTime = (TextView)v.findViewById(R.id.time);
    txtdateTime.setText(this.c.getString(this.c.getColumnIndex("time")));   
    TextView txtdateEvent = (TextView)v.findViewById(R.id.event);
    txtdateEvent.setText(this.c.getString(this.c.getColumnIndex("event")));
    TextView txtdateLocation = (TextView)v.findViewById(R.id.location);
    txtdateLocation.setText(this.c.getString(this.c.getColumnIndex("location")));
    ImageView arrow = (ImageView) v.findViewById(R.id.arrowId);
    arrow.setImageResource(R.drawable.rightarrow);


    Log.i("if chk in db is = 1 then set checked.........",this.c.getString(this.c.getColumnIndex("checked")) +" " +this.c.getString(this.c.getColumnIndex("time")));        


    /*
     * Controls action based on clicked list item (background)
     */
    View lv = v.getRootView(); 
    lv.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View lv) {
            CheckBox cBox = (CheckBox) lv.findViewById(R.id.bcheck);

            // id holds the rowid of each event.  pass this to a new activity to query for description

            // Call Event Detail
            String id = cBox.getTag().toString();
            Intent i = new Intent(context, EventDetail.class);
            //i.putExtra("description", c.getString(c.getColumnIndex("description")));
            i.putExtra("_id", id);
            context.startActivity(i);

        }

    });

    /*
     * Begin - Controls action based on clicked Text only

    txtdateEvent.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            CharSequence charseq = "Darth Vader is alive";
            Toast.makeText(context, charseq, Toast.LENGTH_SHORT).show();
        }

    });

    * End - Controls action based on clicked Text only
    */


    /*
     * Controls action based on clicked checkbox 
     */
    cBox.setOnClickListener(new OnClickListener() {  
        @Override
        public void onClick(View v) {
            EventDbAdapter mDbHelper = new EventDbAdapter(context);
            mDbHelper.open(); 

            CheckBox cBox = (CheckBox) v.findViewById(R.id.bcheck);
            if (cBox.isChecked()) {
                //cBox.setChecked(false);
                CharSequence charseq = "Added to My Schedule";
                Toast.makeText(context, charseq, Toast.LENGTH_SHORT).show();

                // Update the database for each checked item
                mDbHelper.updateChecked(cBox.getTag().toString(), "1");     
                c.requery();

                // Verify that the db was updated for debugging purposes
                String event = c.getString(c.getColumnIndex("event"));                  
                int id = (Integer) cBox.getTag();

                Log.i("checked _id...........", "id= " + id + " " +c.getString(c.getColumnIndex("_id"))); 
                Log.i("checked checked...........", ""+c.getString(c.getColumnIndex("checked")));

            } else if (!cBox.isChecked()) {
                //cBox.setChecked(true);
                CharSequence charseq = "Removed from My Schedule";
                Toast.makeText(context, charseq, Toast.LENGTH_SHORT).show();
                // checkList.remove(cBox.getTag());
                //checkList.add((Integer) cBox.getTag());
                String event = c.getString(c.getColumnIndex("event"));
                //int id = c.getInt(c.getColumnIndex("_id"));
                int id = (Integer) cBox.getTag();
                mDbHelper.updateChecked(cBox.getTag().toString(), "0"); 
                c.requery();
                //int sqlresult = mDbHelper.selectChk(id, event);                   
                //Log.i("sqlresult checked value after update...........", ""+ sqlresult);                  
                //Log.i("unchecked _id...........", ""+c.getString(c.getColumnIndex("_id"))); 
                //Log.i("unchecked checked...........", ""+c.getString(c.getColumnIndex("checked")));
            }
            //mDbHelper.close();
        }
    });

    return(v);
}

}