当使用 CursorAdapter 的回调时,调用 initLoader() 只工作一次

Calling initLoader() only works once when using callbacks for CursorAdapter

在我的 Activity 中,我有一个代表医生的列表视图。每行都有医生的名字和一个复选框。我已经实现了一个回调接口,这样当一个医生被 selected 时,所有其他医生都会从列表视图中删除,只有 selected 医生保留。

它似乎有效,因为一旦我select一个医生,所有其他人都被删除了。当我取消选中医生时,每个人都会被添加回来。但是,如果我现在 select 一个 不同的 医生,原来的那个会留下来,其他的都被移除。

为了更好地解释这个问题,假设当我启动 activity 列表视图时,我有两个医生,Joel 和 Sam。我想我想 select Joel,所以我这样做了,然后 Sam 从列表中删除了。然后,我意识到我错了,所以我取消了 select Joel,现在我在列表中看到了 Joel 和 Sam。最后,我 select 山姆。但是,Sam 被从列表中删除,只剩下 Joel。

这是来自适配器的一些代码片段 class:

@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();
}

在我的 activity 中,我有以下实现:

@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 是一个代表 table 中所有医生的 CursorLoader。 SELECTED_DOCTOR_ID 是一个仅供一个医生使用的 CursorLoader。

如果非要我猜的话,我的问题出在 bindView 方法上,因为我将 id 变量声明为 final。我这样做的原因是否则我会收到一个编译器错误:

error: local variable id is accessed from within inner class; needs to be declared 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

所以,它似乎确实看到我 select Sam,id 为 2,并将 id 2 传递给 initLoader() 方法,但只显示 Joel(医生 id 1)在列表视图中,因为我 select 编辑了他 首先 .

编辑 2

根据请求,这里是 CursorLoader 方法的片段:

@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

这不是错字。似乎 不是 医生第二次被 select 调用。我试过在 onDoctorChecked 中调用 destroyLoader() 但这似乎没有什么不同。

根据 initLoader() documentation:

Ensures a loader is initialized and active. If the loader doesn't already exist, one is created and (if the activity/fragment is currently started) starts the loader. Otherwise the last created loader is re-used.

In either case, the given callback is associated with the loader, and will be called as the loader state changes. If at the point of call the caller is in its started state, and the requested loader already exists and has generated its data, then callback onLoadFinished(Loader, D) will be called immediately (inside of this function), so you must be prepared for this to happen.

initLoader()只对某个loader ID初始化一次,以后重复使用数据。如果您想放弃并重新创建一个新的加载器(即使用新的 CursorLoader),请使用 restartLoader() 代替它(注意:restartLoader() 将初始化加载器,就像initLoader() 第一次,所以你不需要围绕第一个 运行).

的任何特殊逻辑