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();
}
});
Funziona come un fascino. Grazie mille! Quindi mi mancava solo 'swapCursor (cursore);'! Ho aggiunto la soluzione al mio post. – kaolick
Hai menzionato 'su un thread in background, per favore'. Sono curioso di sapere se stai facendo questo in 'AsyncTask' contro un' Loader'? – theblang
@mattblang: Se i tuoi dati sono serviti da un 'ContentProvider' per altri motivi, usa un' CursorLoader'. Altrimenti, usa solo un 'AsyncTask', IMHO. – CommonsWare