そこで、リスト行のカスタム レイアウトを処理するカスタム カーソル アダプターを作成しました。レイアウトには、ToggleButton、ImageButton、および TextView が含まれます。TextView データは ALARM_TIME という列から派生し、正常にロードされます。
ただし、ToggleButton が問題を引き起こしています。その状態は ALARM_ISACTIVE と呼ばれる列から導出され、1 (「オン」に対応) または 0 (「オフ」に対応) のいずれかになります。ToggleButton をクリックすると、それに応じてデータベース内の対応する行が更新されます。
しかし、どういうわけか、私の実際の行動はそれとはまったく異なるものです.
ToggleButton をクリックすると、どの行をクリックしたかに関係なく、クリックした行とリストの最初の 2 行の両方に対して OnCheckedChange リスナーがトリガーされます。さらに、行をチェックして下にスクロールし、上にスクロールすると、行の状態が保持されません。
ここで何が起こっているのかわからず、説明または修正する方法がわかりません。
誰でもここで私を助けることができますか?
参考までに、私のカーソルアダプターは次のとおりです。
public class AlarmCursorAdapter extends SimpleCursorAdapter implements OnCheckedChangeListener {
private Context mContext;
private int mLayout;
private Cursor mCursor;
private int mIdIndex;
private int mAlarmIndex;
private int mIsActiveIndex;
private LayoutInflater mInflater;
public AlarmCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
Log.d("alarmAdapter", "calling constructor");
this.mContext = context;
this.mLayout = layout;
this.mCursor = c;
this.mIdIndex = c.getColumnIndexOrThrow(DailyAlarmTable.ALARM_ID);
this.mAlarmIndex = c.getColumnIndexOrThrow(DailyAlarmTable.ALARM_TIME);
this.mIsActiveIndex = c.getColumnIndexOrThrow(DailyAlarmTable.ALARM_ISACTIVE);
this.mInflater = LayoutInflater.from(mContext);
Log.d("alarmAdapter", "finishing constructor");
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(mCursor.moveToPosition(position)) {
ViewHolder holder;
final int fPosition = position;
Log.d("AlarmCursorAdapter", "getView called for position " + fPosition);
// If the view isn't inflated, we need to create it
if(convertView == null) {
Log.d("AlarmAdapter", "creating position " + fPosition);
convertView = mInflater.inflate(mLayout, null);
holder = new ViewHolder();
holder.alarmView = (TextView) convertView.findViewById(R.id.alarmView);
holder.discardButton = (ImageButton) convertView.findViewById(R.id.alarmDiscard);
holder.isActiveToggle = (ToggleButton) convertView.findViewById(R.id.alarmToggle);
convertView.setTag(holder);
} else { // If the view is already inflated, we need to get it for the holder
Log.d("AlarmAdapter", "recycling position " + fPosition);
holder = (ViewHolder) convertView.getTag();
}
// Populate the views
String alarmString = mCursor.getString(mAlarmIndex);
int isActive = mCursor.getInt(mIsActiveIndex);
final int id = mCursor.getInt(mIdIndex);
holder.alarmView.setText(alarmString);
if(isActive == 1) {
holder.isActiveToggle.setChecked(true);
} else {
holder.isActiveToggle.setChecked(false);
}
holder.isActiveToggle.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton button, boolean isChecked) {
Log.d("ToggleListener", "Toggle for " + fPosition + " triggered");
if(isChecked) {
Toast.makeText(mContext, "row " + fPosition + " is checked", Toast.LENGTH_SHORT).show();
// Change the value of ALARM_ISACTIVE in the cursor to 1
ContentValues newValues = new ContentValues();
newValues.put(DailyAlarmTable.ALARM_ISACTIVE, 1);
String selection = "(" + DailyAlarmTable.ALARM_ID + " = " + id + ")";
int rowsUpdated = 0;
rowsUpdated = mContext.getContentResolver().update(AlarmProvider.CONTENT_URI,
newValues, selection, null);
} else {
Toast.makeText(mContext, "row " + fPosition + " is unchecked", Toast.LENGTH_SHORT).show();
// Change the value of ALARM_ISACTIVE in the cursor to 0
ContentValues newValues = new ContentValues();
newValues.put(DailyAlarmTable.ALARM_ISACTIVE, 0);
String selection = "(" + DailyAlarmTable.ALARM_ID + " = " + id + ")";
int rowsUpdated = 0;
rowsUpdated = mContext.getContentResolver().update(AlarmProvider.CONTENT_URI,
newValues, selection, null);
}
}
});
}
return convertView;
}
static class ViewHolder {
ToggleButton isActiveToggle;
TextView alarmView;
ImageButton discardButton;
}
@Override
public void onCheckedChanged(CompoundButton button, boolean isChecked) {
// TODO Auto-generated method stub
}
}
これは、私のコンテンツ プロバイダーの update() メソッドです。徹底的にするためにこれを含めています.notifyChange()を呼び出すべきではないことがわかるまで、問題ははるかに悪化していました(行をクリックするとすぐに、行は互いに応答して無限に更新され続けました):
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int uriType = sUriMatcher.match(uri);
SQLiteDatabase db = database.getWritableDatabase();
int rowsUpdated = 0;
switch(uriType) {
case ALARMS:
rowsUpdated = db.update(DailyAlarmTable.TABLE_ALARM,
values,
selection,
selectionArgs);
break;
case ALARM_ID:
String id = uri.getLastPathSegment();
if(TextUtils.isEmpty(selection)) {
rowsUpdated = db.update(DailyAlarmTable.TABLE_ALARM,
values,
DailyAlarmTable.ALARM_ID + "=" + id,
null);
} else {
rowsUpdated = db.update(DailyAlarmTable.TABLE_ALARM,
values,
DailyAlarmTable.ALARM_ID + "=" + id + " and " + selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
// getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}