如何从CursorLoader中删除除已检查项以外的所有项?

时间:2015-01-04 20:28:55

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

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

在适配器类中,我有以下代码片段:

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

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

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的活动,以将数据加载到listview中。在活动中,我可以致电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);
    }
});

但它没有做我想要的。

3 个答案:

答案 0 :(得分:1)

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

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(),然后我重新查询,依此类推。现在,当我检查特定医生的复选框时,所有其他人都将从列表中删除。感谢所有花时间研究这个问题的人。


修改

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

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,它只能处理一个医生,而且该加载器将替换前一个(所有医生)。再次感谢所有的帮助和建议。

答案 1 :(得分:-1)

您需要做的是从光标中删除选中的项目,但不能直接执行此操作,因此您需要ContentResolver#delete()来电或SQL

编辑:

您无法仅从光标中删除。您需要重新查询筛选结果,以便在更改选中状态后,还可以更改标记

答案 2 :(得分:-1)

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

1)使用FilterQueryProvider 您使用查询值设置filterQueryProvider,如果运行它将选择指定的医生。选中医生后,您将filterQueryProvider设置为适配器constraint value,例如等于doctor id。在runQuery的{​​{1}}中,您必须返回一个光标,选择具有filterQueryProvider的医生。然后每次检查医生时,您只需致电constraint id

你还应该覆盖adapter.getFilter().filter(doctoreId),因此它不会关闭前一个光标(由加载器返回的光标),因为关闭光标是加载器的责任。 因此,在您的情况下检查光标大小,如果它等于一个关闭它,但如果不是,请不要关闭它。

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