2013-06-05 16 views
9

Questa potrebbe essere una domanda noob, ma sono abbastanza nuova in tutto questo SQLite-Database-Cursor-Adapter-ListView-Do-It-Proper-Stuff.Android: come richiedere un cursore per aggiornare ListView dopo aver eliminato la riga del database?

quello che ho:

Nel mio MainActivity Ho un ListView. Io uso un SQLite database e popolare il ListView con un adattatore personalizzato che estende SimpleCursorAdapter. Facendo clic su un elemento nel mio ActionBar Attivo Contextual Action Mode. Tutto funziona fino ad ora.

quello che voglio:

Cliccando su una determinata icona nella mia ListView item riga database in base va cancellato e il ListView dovrebbe essere aggiornata.

La mia domanda:

Come posso aggiornare il mio Cursor e la mia ListView correttamente? Quando io non uso cursor.requery() nella mia OnClickListener e utilizzare cursor = dbm.getIOIOSensorsCursor() invece ho un CursorIndexOutOfBoundsException poche righe sotto alla linea di

int state = cursor.getInt(cursor.getColumnIndex(IOIOSensorSchema.STATE)); 

mia applicazione si blocca, ma dopo aver ricaricato che il database è stato eliminato e il secondo è ListView item andato.

Suppongo che l'arresto anomalo debba avere a che fare con _position nel metodo getView perché _position è final. Tuttavia, quando uso cursor.requery(), tutto funziona come dovrebbe.

Ma questo metodo è deprecato e la documentazione dice "Non usare questo ...". Sono un amico della programmazione corretta (sono ancora un principiante e voglio imparare a codificare nel modo giusto e non veloce e sporco) e voglio sapere come farlo nel modo giusto. Non so se è importante, ma sto testando la mia app solo sul mio (molto veloce) Nexus 4. Sembra che non ci siano problemi con l'aggiornamento dello Cursor abbastanza veloce, ma mi chiedo se funzionerà su dispositivi più lenti. Nel caso in cui sia importante per te il mio database conterrà circa 10-20 righe con circa 12 colonne. Immagino che questo sia un database davvero piccolo.

Ecco il codice rilevante del mio adattatore personalizzato:

public class IOIOSensorCursorAdapterCam extends SimpleCursorAdapter 
{ 
static class ViewHolder 
{ 
ImageView stateIV, removeIV; 
TextView nameTV, pinNumberTV, feedIDTV, freqTV; 
} 

private Context ctx; 
private Cursor cursor; 
private IodDatabaseManager dbm; 

public IOIOSensorCursorAdapterCam(Context _context, int _layout, 
    Cursor _cursor, String[] _from, int[] _to, int _flags) 
{ 
super(_context, _layout, _cursor, _from, _to, _flags); 
ctx = _context; 
cursor = _cursor; 
dbm = new IodDatabaseManager(_context); 
} 

@Override 
public View getView(final int _position, View _convertView, 
    ViewGroup _parent) 
{ 
ViewHolder holder = null; 

LayoutInflater inflater = (LayoutInflater) ctx 
    .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 

// There is no view at this position, we create a new one. In this case 
// by inflating an xml layout. 
if (_convertView == null) 
{ 
    // Inflate a layout 
    _convertView = inflater.inflate(R.layout.listview_item_sensor_cam, 
     null); 

    holder = new ViewHolder(); 
    holder.stateIV = (ImageView) _convertView 
     .findViewById(R.id.stateImageView); 
    holder.nameTV = (TextView) _convertView 
     .findViewById(R.id.sensorNameTextView); 
    holder.pinNumberTV = (TextView) _convertView 
     .findViewById(R.id.sensorPinNumberTextView); 
    holder.feedIDTV = (TextView) _convertView 
     .findViewById(R.id.sensorFeedIDTextView); 
    holder.freqTV = (TextView) _convertView 
     .findViewById(R.id.sensorFrequencyTextView); 
    holder.removeIV = (ImageView) _convertView 
     .findViewById(R.id.removeImageView); 
    _convertView.setTag(holder); 
} 
// We recycle a View that already exists. 
else 
{ 
    holder = (ViewHolder) _convertView.getTag(); 
} 

// Set an OnClickListener to the "Delete Icon" 
holder.removeIV.setOnClickListener(new OnClickListener() 
{ 
    @SuppressWarnings("deprecation") 
    @Override 
    public void onClick(View _view) 
    { 
    cursor.moveToPosition(_position); 

    // Delete sensor from database here 
    int sensorID = cursor.getInt(cursor 
     .getColumnIndex(IOIOSensorSchema.SENSOR_ID)); 
    dbm.deleteIOIOSensor(sensorID); 

    // This leads to a "CursorIndexOutOfBoundsException" and cannot 
    // be used to refresh the ListView 
//  cursor = dbm.getIOIOSensorsCursor(); 

    // Refresh ListView 
    cursor.requery(); 
    notifyDataSetChanged(); 
    } 
}); 

cursor.moveToPosition(_position); 

if (cursor.getCount() > 0) 
{ 
    int state = cursor.getInt(cursor 
     .getColumnIndex(IOIOSensorSchema.STATE)); 

    if (state == 0) 
    { 
    holder.stateIV.setImageResource(R.drawable.av_play_over_video); 
    holder.stateIV.setColorFilter(ctx.getResources().getColor(
     R.color.hint_lighter_gray)); 
    // _convertView.setAlpha((float) 0.5); 
    holder.nameTV.setTextColor(ctx.getResources().getColor(
     R.color.hint_darker_gray)); 
    } 
    else 
    { 
    holder.stateIV.setImageResource(R.drawable.av_pause_over_video); 
    holder.stateIV.setColorFilter(ctx.getResources().getColor(
     android.R.color.holo_green_light)); 
    // _convertView.setAlpha((float) 1); 
    holder.nameTV.setTextColor(ctx.getResources().getColor(
     android.R.color.black)); 
    } 

    // Set the sensor's name to the according TextView 
    String sensorName = cursor.getString(cursor 
     .getColumnIndex(IOIOSensorSchema.NAME)); 
    holder.nameTV.setText(sensorName); 

    // Set the sensor's pin number to the according TextView 
    int pinNumber = cursor.getInt(cursor 
     .getColumnIndex(IOIOSensorSchema.PIN_NUMBER)); 
    holder.pinNumberTV.setText("" + pinNumber); 

    // Set the sensor's feed ID to the according TextView 
    int feedID = cursor.getInt(cursor 
     .getColumnIndex(IOIOSensorSchema.FEED_ID)); 
    holder.feedIDTV.setText("" + feedID); 

    // Set the sensor's frequency to the according TextView 
    int frequency = cursor.getInt(cursor 
     .getColumnIndex(IOIOSensorSchema.FREQUENCY)); 
    int timeUnit = cursor.getInt(cursor 
     .getColumnIndex(IOIOSensorSchema.TIME_UNIT)); 
    String frequencyTextViewText = ""; 
    switch (timeUnit) 
    { 
    case IodIOIOSensor.TIME_UNIT_MINUTES: 
    frequencyTextViewText = frequency + " min"; 
    break; 
    case IodIOIOSensor.TIME_UNIT_HOURS: 
    frequencyTextViewText = frequency + " h"; 
    break; 
    default: 
    frequencyTextViewText = frequency + " sec"; 
    break; 
    } 
    holder.freqTV.setText(frequencyTextViewText); 
} 
return _convertView; 
} 
} 

Edit:

Ecco il mio codice rilevante dal OnCickListener dopo l'implementazione della soluzione:

// Set an OnClickListener to the "Delete Icon" 
holder.removeIV.setOnClickListener(new OnClickListener() 
{ 
    @Override 
    public void onClick(View _view) 
    { 
    cursor.moveToPosition(_position); 

    // Delete sensor from database here 
    int sensorID = cursor.getInt(cursor 
     .getColumnIndex(IOIOSensorSchema.SENSOR_ID)); 
    dbm.deleteIOIOSensor(sensorID); 

    Toast.makeText(ctx, R.string.toast_sensor_deleted, 
     Toast.LENGTH_SHORT).show(); 

    // Refresh ListView 
    cursor = dbm.getIOIOSensorsCursor(); 
    swapCursor(cursor); 

    notifyDataSetChanged(); 
    } 
}); 

risposta

15

Come aggiornare la mia C ursor e il mio ListView correttamente?

You "rinfrescare [tua] Cursore" eseguendo il codice nuovo per ottenere il Cursor, utilizzando il codice utilizzato per creare l'originale Cursor (su un thread in background, per favore). Aggiorna il tuo ListView chiamando changeCursor() o swapCursor() su CursorAdapter.

+0

Funziona come un fascino. Grazie mille! Quindi mi mancava solo 'swapCursor (cursore);'! Ho aggiunto la soluzione al mio post. – kaolick

+0

Hai menzionato 'su un thread in background, per favore'. Sono curioso di sapere se stai facendo questo in 'AsyncTask' contro un' Loader'? – theblang

+0

@mattblang: Se i tuoi dati sono serviti da un 'ContentProvider' per altri motivi, usa un' CursorLoader'. Altrimenti, usa solo un 'AsyncTask', IMHO. – CommonsWare