如何从 CursorLoader 中删除除选中项以外的所有项?
How to remove all but the checked item from a CursorLoader?
我有一个 activity 可以为用户 select 显示医生的列表视图。每个列表视图项都包含一个复选框和一个医生的名字。我的最终目标是当用户检查医生时,所有其他人都将被删除,只剩下 selected 医生。
在适配器类中,我有以下代码片段:
viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked) {
swapCursor(cursor);
}
}
});
我有存储前一个光标的代码,因此如果医生未被选中,它可以恢复,但我删除了它以简化这个问题。目前,当我选中复选框时没有任何反应。我尝试添加 notifyDataSetChanged()
但适配器没有注册观察者,因此没有任何区别。我怎样才能让 activity 知道某项已被 select 编辑?
从 documentation 我知道我应该注册一个 DataSetObserver
但我不知道应该注册哪个对象以及如何注册。
编辑
这是我的适配器类的更多内容:
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);
}
}
适配器用于 activity,它实现了 CursorLoader 以将数据加载到列表视图中。在 activity 中,我可以打电话给 doctorAdapter.swapCursor(someCursorIWant);
但我不能参考 selected 医生。我试过的东西:
// 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();
}
}
});
然后在activity:
// Set observers
mDoctorAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
Cursor c = mDoctorAdapter.getCheckedDoctor();
if(c != null)
mDoctorAdapter.swapCursor(c);
}
});
但它并没有按照我的意愿进行。
您需要做的是从光标中删除选中的项目,但您不能直接这样做,因此您需要 ContentResolver#delete()
调用或 SQL
编辑:
不能只从光标处删除。需要重新查询过滤后的结果,这样你改变checked状态后,也改变一个flag
您可以通过两种方式实现:
1) 使用 FilterQueryProvider
您设置一个 filterQueryProvider
和一个查询值,如果它运行它将 select 指定的医生。当检查医生时,您将 filterQueryProvider
设置为带有 constraint value
的适配器,例如等于 doctor id
。在 filterQueryProvider
的 runQuery
中,你必须 return 一个游标,select 一个有 constraint id
的医生。然后每次检查医生时,您只需致电 adapter.getFilter().filter(doctoreId)
您还应该重写 changeCursor
,这样它就不会关闭前一个游标 (加载程序 returns 的游标) 因为关闭了游标是装载机的责任。
因此,在您的情况下,请检查光标大小,如果它等于 1,则关闭它,但如果不是,则不要关闭它。
2) 向您的内容提供者添加一个新的 uri,该内容提供者用于 return 只是一个医生的 ID。然后,当用户检查新医生时,您会调用一个新的 initLoader,其中包含加载器 ID 和 select 医生所需的信息包。
我已经解决了这个问题。我所做的是调整 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();
}
});
}
然后,我在 activity 中注册了一个新的观察者,在 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()
,然后我重新查询,依此类推。现在,当我选中特定医生的复选框时,所有其他医生都会从列表中删除。感谢所有花时间研究这个的人。
编辑
根据评论中的建议,我将其更改为使用回调。在我的适配器 class 中,我包含了以下接口和方法:
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);
}
并且在我的 activity(实现接口)中添加了以下代码:
// 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,旨在仅处理一个医生,并且该加载器将替换前一个(所有医生中的)。再次感谢所有的帮助和建议。
我有一个 activity 可以为用户 select 显示医生的列表视图。每个列表视图项都包含一个复选框和一个医生的名字。我的最终目标是当用户检查医生时,所有其他人都将被删除,只剩下 selected 医生。
在适配器类中,我有以下代码片段:
viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked) {
swapCursor(cursor);
}
}
});
我有存储前一个光标的代码,因此如果医生未被选中,它可以恢复,但我删除了它以简化这个问题。目前,当我选中复选框时没有任何反应。我尝试添加 notifyDataSetChanged()
但适配器没有注册观察者,因此没有任何区别。我怎样才能让 activity 知道某项已被 select 编辑?
从 documentation 我知道我应该注册一个 DataSetObserver
但我不知道应该注册哪个对象以及如何注册。
编辑
这是我的适配器类的更多内容:
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);
}
}
适配器用于 activity,它实现了 CursorLoader 以将数据加载到列表视图中。在 activity 中,我可以打电话给 doctorAdapter.swapCursor(someCursorIWant);
但我不能参考 selected 医生。我试过的东西:
// 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();
}
}
});
然后在activity:
// Set observers
mDoctorAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
Cursor c = mDoctorAdapter.getCheckedDoctor();
if(c != null)
mDoctorAdapter.swapCursor(c);
}
});
但它并没有按照我的意愿进行。
您需要做的是从光标中删除选中的项目,但您不能直接这样做,因此您需要 ContentResolver#delete()
调用或 SQL
编辑:
不能只从光标处删除。需要重新查询过滤后的结果,这样你改变checked状态后,也改变一个flag
您可以通过两种方式实现:
1) 使用 FilterQueryProvider
您设置一个 filterQueryProvider
和一个查询值,如果它运行它将 select 指定的医生。当检查医生时,您将 filterQueryProvider
设置为带有 constraint value
的适配器,例如等于 doctor id
。在 filterQueryProvider
的 runQuery
中,你必须 return 一个游标,select 一个有 constraint id
的医生。然后每次检查医生时,您只需致电 adapter.getFilter().filter(doctoreId)
您还应该重写 changeCursor
,这样它就不会关闭前一个游标 (加载程序 returns 的游标) 因为关闭了游标是装载机的责任。
因此,在您的情况下,请检查光标大小,如果它等于 1,则关闭它,但如果不是,则不要关闭它。
2) 向您的内容提供者添加一个新的 uri,该内容提供者用于 return 只是一个医生的 ID。然后,当用户检查新医生时,您会调用一个新的 initLoader,其中包含加载器 ID 和 select 医生所需的信息包。
我已经解决了这个问题。我所做的是调整 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();
}
});
}
然后,我在 activity 中注册了一个新的观察者,在 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()
,然后我重新查询,依此类推。现在,当我选中特定医生的复选框时,所有其他医生都会从列表中删除。感谢所有花时间研究这个的人。
编辑
根据评论中的建议,我将其更改为使用回调。在我的适配器 class 中,我包含了以下接口和方法:
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);
}
并且在我的 activity(实现接口)中添加了以下代码:
// 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,旨在仅处理一个医生,并且该加载器将替换前一个(所有医生中的)。再次感谢所有的帮助和建议。