【问题标题】:Calling initLoader() only works once when using callbacks for CursorAdapter当对 CursorAdapter 使用回调时,调用 initLoader() 仅有效一次
【发布时间】: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 你是对的,我不需要那个。我不认为这与问题有关。我确实相信(不知何故)它可能会受到final id 的影响。因为无论我选择哪个医生,当其他人被检查时,那是唯一留下的医生。
  • 听起来 LoaderManager 正在缓存游标并在您使用相同的加载程序 ID 时返回相同的游标,但我不知道它这样做了(实际上不认为它这样做)
  • 您可以输入日志语句来验证是否使用正确的参数再次调用 onCreateLoader? (没有缓存)

标签: java android callback android-cursoradapter android-cursorloader


【解决方案1】:

根据initLoader() documentation

确保加载程序已初始化并处于活动状态。如果加载器尚不存在,则创建一个并(如果活动/片段当前已启动)启动加载器。 否则最后创建的加载器会被重新使用。

在任何一种情况下,给定的回调都与加载程序相关联,并将在加载程序状态更改时调用。 如果在调用点调用者处于启动状态,并且请求的加载器已经存在并且已经生成了它的数据,那么回调 onLoadFinished(Loader, D) 将立即被调用(在这个函数内部),所以你必须为此做好准备。

initLoader() 只初始化某个加载器 ID 一次,然后重用数据。如果你想扔掉并重新创建一个新的加载器(即,使用新的CursorLoader),请使用restartLoader() 代替它(注意:restartLoader() 将像initLoader() 第一次一样初始化加载器,因此您在第一次运行时不需要任何特殊逻辑)。

【讨论】:

  • 谢谢!我将 onDoctorChecked 方法中的行更改为使用 restartLoader 并且按预期工作。
猜你喜欢
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 2023-02-08
  • 1970-01-01
  • 2012-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多