【问题标题】:How to remove all but the checked item from a CursorLoader?如何从 CursorLoader 中删除除选中项目之外的所有项目?
【发布时间】:2015-01-04 20:28:55
【问题描述】:

我有一个显示医生列表视图供用户选择的活动。每个列表视图项都包含一个复选框和一个医生姓名。我的最终目标是当用户检查一个医生时,所有其他人都被删除,只剩下选定的医生。

在适配器类中,我有以下代码sn-p:

viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if(isChecked) {
            swapCursor(cursor);
        }
    }
});

我有存储前一个光标的代码,因此如果未选中医生,则可以恢复它,但我已将其删除以简化此问题。目前,当我选中复选框时,没有任何反应。我尝试添加notifyDataSetChanged(),但适配器没有注册观察者,所以没有区别。如何让 Activity 知道某个项目已被选中?

documentation 我知道我应该注册一个DataSetObserver 但我不知道应该注册哪个对象以及如何注册。

编辑

以下是我的 Adapter 类中的更多内容:

public MyAdapter(Context context, Cursor cursor, int flags){
    super(context, cursor, flags);
}

@Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
    View view = LayoutInflater.from(context).inflate(R.layout.adapter_layout, viewGroup, false);
    ViewHolder viewHolder = new ViewHolder(view);
    view.setTag(viewHolder);
    return view;
}

@Override
public void bindView(View view, final Context context, final Cursor cursor) {
    ViewHolder viewHolder = (ViewHolder) view.getTag();

    String firstName = cursor.getString(cursor.getColumnIndex(DoctorEntry.COLUMN_FIRSTNAME));
    String lastName = cursor.getString(cursor.getColumnIndex(DoctorEntry.COLUMN_LASTNAME));
    String suffix = cursor.getString(cursor.getColumnIndex(DoctorEntry.COLUMN_SUFFIX));

    viewHolder.nameView.setText(firstName + " " + lastName + ", " + suffix);

    // Should I add something here?
    viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

        }
    });
}

public static class ViewHolder{
    public final CheckBox checkBox;
    public final TextView nameView;

    public ViewHolder(View view){
        checkBox = (CheckBox) view.findViewById(R.id.chooseDoctorCheckBox);
        nameView = (TextView) view.findViewById(R.id.chooseDoctorLabel);
    }
}

适配器用于实现 CursorLoader 以将数据加载到列表视图的活动中。在活动中,我可以拨打doctorAdapter.swapCursor(someCursorIWant);,但我无法引用所选医生。我尝试过的东西:

// Set currentDoctor (a property of the adapter class) if a new one is checked.
// Again, other checks removed for simplicity.
viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if(isChecked){
            currentDoctor = cursor;
            notifyDataSetChanged();
        }
    }
});

然后在活动中:

// Set observers
mDoctorAdapter.registerDataSetObserver(new DataSetObserver() {
    @Override
    public void onChanged() {
        Cursor c = mDoctorAdapter.getCheckedDoctor();
        if(c != null)
           mDoctorAdapter.swapCursor(c);
    }
});

但它并没有达到我想要的效果。

【问题讨论】:

  • 你在使用 CursorLoader 吗?
  • 是的,在活动中我有几行调用doctorAdapter.swapCursor(someCursor),但如果我尝试在适配器类本身中调用它,它将不起作用。我无法将此代码移动到活动中,因为我不知道如何从活动中引用复选框侦听器。
  • 将其注册到您的适配器。我从未使用过 android 适配器,但在 API 文档中我看到了 registerDataSetObserver(DataSetObserver) 方法。你不能用那个吗?不确定我是否正确理解了这个问题
  • 你在 CursorAdapter 构造函数中使用了哪些标志?
  • 标志为 0,@VinceEmigh 我应该注册什么?我在活动中没有任何 DataSetObserver 对象,也找不到任何可以转换为该类型的对象。

标签: java android listview android-sqlite android-cursoradapter


【解决方案1】:

我已经解决了这个问题。我所做的是调整bindView() 方法以在选择医生时存储医生的ID:

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));
    String lastName = cursor.getString(cursor.getColumnIndex(DoctorEntry.COLUMN_LASTNAME));
    String suffix = cursor.getString(cursor.getColumnIndex(DoctorEntry.COLUMN_SUFFIX));

    viewHolder.nameView.setText(firstName + " " + lastName + ", " + suffix);

    viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if(isChecked){
                selectedDoctorId = id;
            } else{
                selectedDoctorId = 0;
            }
            notifyDataSetChanged();
        }
    });
}

然后,我在活动中注册了一个新的观察者,并在 onChanged() 方法中提取 id 并使用新的 Uri 来查询该医生:

// Add observer for doctor
mDoctorAdapter.registerDataSetObserver(new DataSetObserver() {
    @Override
    public void onChanged() {
        if(!systemEntered && mDoctorAdapter.getSelectedDoctorId() != 0){
            Cursor c = getContentResolver().query(
                    DoctorEntry.buildDoctorUri(mDoctorAdapter.getSelectedDoctorId()),
                    DOCTOR_COLUMNS,
                    null,
                    null,
                    null
            );
            systemEntered = true;
            mDoctorAdapter.swapCursor(c);
            systemEntered = false;
        }
    }
});

我必须使用一个名为systemEntered 的布尔标志来防止无限循环。没有它,调用swapCursor() 再次触发onChanged(),然后我重新查询,依此类推。现在,当我选中特定医生的复选框时,所有其他医生都会从列表中删除。感谢所有花时间研究此问题的人。


编辑

根据 cmets 的建议,我已将其更改为使用回调。在我的适配器类中,我包含了以下接口和方法:

private DoctorAdapterCallbacks mCallbacks;

viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if(isChecked && mCallbacks != null){
            mCallbacks.onDoctorChecked(id);
        }
    }
});

public void onRegisterCallbacks(Activity activity){
    mCallbacks = (DoctorAdapterCallbacks) activity;
}

public static interface DoctorAdapterCallbacks{
    void onDoctorChecked(long id);
}

在我的活动(实现接口)中,我添加了以下代码:

// Set callbacks (This is in the onCreate method)
mDoctorAdapter.onRegisterCallbacks(this);

@Override
public void onDoctorChecked(long id) {
    Bundle args = new Bundle();
    args.putLong(SELECTED_DOCTOR_ID, id);
    getSupportLoaderManager().initLoader(SELECTED_DOCTOR_LOADER, args, this);
}

我创建了另一个 CursorLoader,它只用于处理单个医生,并且该加载器将替换前一个(所有医生)。再次感谢所有帮助和建议。

【讨论】:

  • 您应该使用CursorLoader 来查询您需要的行,而不是在主线程上执行查询——毕竟这就是使用CursorLoader 的全部意义所在。最好使用其他方式通知Activity 重新查询,而不是使用虚拟数据集更改通知和观察者。
  • @corsair992 我想我可以创建一个新的加载程序(或找到一种方法来操作我现有的医生加载程序),但我找不到任何其他方式来通知 Activity。
  • 你可以设置一个自定义的监听回调接口,并从Activity注册。或者,如果您想以一种更简单但更简单的方式进行操作,您可能只需将 Context 转换为您的 Activity 子类,然后在其上调用任何自定义方法。
  • @corsair992 类似Activity和Navigation Drawer的回调的关系?
  • 是的。或者像View 上的OnClickListener(或OnCheckedChangeListener)接口。你只需定义一个带有回调方法的接口,在适配器类中提供注册方法,从Activity实现并注册回调,并将相关事件通知注册的监听器
【解决方案2】:

你需要做的是从光标中删除选中的项目,但你不能直接这样做,所以你需要ContentResolver#delete()调用或SQL

编辑:

你不能只从光标处删除。你需要重新查询过滤后的结果,这样在你改变checked状态后,你也改变了一个flag

【讨论】:

  • 这也会从数据库中删除它,而不仅仅是光标。
  • 删除选中的项目与我想做的相反。
【解决方案3】:

您可以通过两种方式实现它:

1)使用FilterQueryProvider 您设置一个带有查询值的filterQueryProvider,如果它运行它将选择指定的医生。当检查医生时,您将filterQueryProvider 设置为具有constraint value 的适配器,例如等于doctor id。在runQueryfilterQueryProvider 中,您必须返回一个光标,该光标选择具有constraint id 的医生。那么每次检查医生时,您只需致电adapter.getFilter().filter(doctoreId)

你还应该覆盖changeCursor,这样它就不会关闭前一个游标(加载器返回的那个),因为关闭游标是加载器的责任。 因此,在您的情况下,请检查光标大小,如果它等于 1,请关闭它,但如果不是,请不要关闭它。

2) 向您的内容提供者添加一个新的 uri,用于通过他的 id 返回一个医生。然后当用户检查一个新医生时,你调用一个新的 initLoader 加载器 ID 和所需的信息包来选择医生。

【讨论】:

  • 我已经在适配器上使用了FilterQueryProvider,以允许用户通过输入文本视图来过滤医生。是否可以添加第二个?另外 - 我已经有一个 Uri 可以通过特定 id 查询医生,我只是不知道在选中复选框时如何调用它。
  • 对于第一部分,我认为您可以创建一个扩展FilterQueryProvider 的类,它接受不同的参数(constraint),其中一个是doctoreId,所以如果检查了医生,您只需调用adapter.getFilter().filter(doctoreId)并且因为在该课程的runQuery 内,您将收到doctoreId,您也可以获得正确的医生(使用新光标过滤列表)。
  • 对于第二部分,您可以简单地运行restartLoader,并将 docterId 作为传递给函数的包。然后在 onCreateLoader 中,您将检查捆绑包,如果它具有医生 ID,您将使用选择一位医生的 URI。
猜你喜欢
  • 2021-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-13
  • 1970-01-01
相关资源
最近更新 更多