【发布时间】:2015-01-12 02:37:18
【问题描述】:
在我的活动中,我有一个代表医生的列表视图。每行都有医生的姓名和一个复选框。我已经实现了一个回调接口,这样当一个医生被选中时,所有其他医生都会从列表视图中删除,只剩下选中的医生。
它似乎有效,因为一旦我选择了一名医生,所有其他人都会被删除。当我取消选中医生时,每个人都会被添加回来。但是,如果我现在选择一个不同的医生,原来的医生会留下来,而其他所有的医生都会被删除。
为了更好地解释这个问题,假设当我开始活动时,我在列表视图中有两个医生,Joel 和 Sam。我想我想选择 Joel,所以我选择了,并且 Sam 从列表中删除。然后,我意识到我错了,所以我取消选择 Joel,现在我在列表中看到了 Joel 和 Sam。最后,我选择山姆。但是,Sam 已从列表中删除,并且只剩下 Joel。
这是来自适配器类的一些代码 sn-ps:
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder viewHolder = (ViewHolder) view.getTag();
final long id = cursor.getLong(cursor.getColumnIndex(DoctorEntry._ID));
String firstName = cursor.getString(cursor.getColumnIndex(DoctorEntry.COLUMN_FIRSTNAME));
viewHolder.nameView.setText(firstName);
viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(mCallbacks != null){
if(isChecked){
mCallbacks.onDoctorChecked(id);
} else{
mCallbacks.onDoctorUnchecked();
}
}
}
});
}
public void onRegisterCallbacks(DoctorAdapterCallbacks activity){
mCallbacks = activity;
}
public static interface DoctorAdapterCallbacks{
void onDoctorChecked(long id);
void onDoctorUnchecked();
}
在我的活动中,我有以下实现:
@Override
public void onDoctorChecked(long id) {
Bundle args = new Bundle();
args.putLong(SELECTED_DOCTOR_ID, id);
getSupportLoaderManager().initLoader(SELECTED_DOCTOR_LOADER, args, this);
}
@Override
public void onDoctorUnchecked() {
getSupportLoaderManager().initLoader(DOCTOR_LOADER, null, this);
}
DOCTOR_LOADER 是一个 CursorLoader,代表表中的所有医生。 SELECTED_DOCTOR_ID 是一个仅供单个医生使用的 CursorLoader。
如果我不得不猜测,我的问题在于 bindView 方法,因为我将 id 变量声明为 final。我这样做的原因是因为否则我会收到编译器错误:
错误:从内部类中访问局部变量 id;需要声明为final
声明变量final 是否会造成麻烦?有没有人发现问题?
编辑
我在适配器的onCheckedChangedListener 和Activity 的onDoctorSelected 中都添加了日志语句。使用上面的相同示例,我看到以下输出:
> onCheckedChanged : Selecting doctor id: 1 // Joel
> onDoctorSelected : Selecting doctor id: 1 // Joel
> onCheckedChanged : Selecting doctor id: 2 // Sam
> onDoctorSelected : Selecting doctor id: 2 // Sam
所以,它似乎确实看到我选择了 id 为 2 的 Sam,并将 id 2 传递给 initLoader() 方法,但列表视图中只显示了 Joel(医生 id 1),因为我首先选择了他。
编辑 2
根据请求,这里是 CursorLoader 方法的 sn-p:
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
switch(i){
case DOCTOR_LOADER:
return new CursorLoader(
this,
DoctorEntry.CONTENT_URI,
DOCTOR_COLUMNS,
null,
null,
null
);
case SELECTED_DOCTOR_LOADER:
long _id = bundle.getLong(SELECTED_DOCTOR_ID);
return new CursorLoader(
this,
DoctorEntry.buildDoctorUri(_id),
DOCTOR_COLUMNS,
DoctorEntry._ID + " = '" + _id + "'",
null,
null
);
default:
throw new UnsupportedOperationException("Unknown loader id: " + i);
}
}
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
switch(cursorLoader.getId()){
case DOCTOR_LOADER:
case SELECTED_DOCTOR_LOADER:
mDoctorAdapter.swapCursor(cursor);
break;
default:
throw new UnsupportedOperationException("Unknown loader id: " + cursorLoader.getId());
}
}
编辑 3
我在onCreateLoader 中添加了另一个日志语句,以查看正在使用哪个 ID 创建加载程序。然后,我看到了这个输出:
> onCheckedChanged : Selecting doctor id: 2
> onDoctorSelected : Selecting doctor id: 2
> onCreateLoader : Using id: 2
> // Unchecked, and now check doctor one
> onCheckedChanged : Selecting doctor id: 1
> onDoctorSelected : Selecting doctor id: 1
这不是错字。在第二次选择医生时,似乎不调用了 onCreateLoader。我曾尝试在onDoctorChecked 内调用destroyLoader(),但这似乎没有什么不同。
【问题讨论】:
-
我看到的唯一有点有趣的是将基于 ID 的 URI 和选择标准都传递给游标加载器。您的内容提供者应该只需要该 URI 就可以知道要返回哪个医生。我想知道问题是否出在您的内容提供商上?也许你也可以发帖
-
@GregEnnis 老实说,我正在关注我之前看过的一个例子。你是对的,如果我将 ID 放在 URI 中,为什么还需要选择标准?我会在没有的情况下尝试一下,看看它的工作方式是否有任何不同。
-
@GregEnnis 你是对的,我不需要那个。我不认为这与问题有关。我确实相信(不知何故)它可能会受到
finalid 的影响。因为无论我选择哪个医生先,当其他人被检查时,那是唯一留下的医生。 -
听起来 LoaderManager 正在缓存游标并在您使用相同的加载程序 ID 时返回相同的游标,但我不知道它这样做了(实际上不认为它这样做)
-
您可以输入日志语句来验证是否使用正确的参数再次调用 onCreateLoader? (没有缓存)
标签: java android callback android-cursoradapter android-cursorloader