如何在设备旋转时恢复 Android RecyclerView 列表项中的复选框状态
How to restore checkBox state in Android RecyclerView list items on Rotation of device
我当前的 Android 应用程序包含一个 RecyclerView,列表中的每个项目都有一个复选框。
我的问题是在屏幕旋转时,复选框总是 returns 处于未选中状态。
我将更新的列表项存储在我的活动 ViewModel 中,并将保存的列表传递回 onCreate 中的适配器
从我的调试日志中我可以看到列表状态正确地保存了每个列表项的 checked/unchecked 状态。
我还注销了适配器构造函数中的列表,该列表也显示检查状态仍然有效。
然而,当调用 onBindViewHolder
时,列表项都未选中。
这是我的代码片段:-
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout);
manageViewModel();
recyclerView = findViewById(R.id.my_rv);
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
adapter = new MedicationAdapter(viewModel.getList(), this);
recyclerView.setAdapter(adapter);
}
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
viewModel.saveList(adapter.getList());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
}
我的列表项checkBox在xml中定义如下:-
<android.support.v7.widget.AppCompatCheckBox
android:id="@+id/my_checkbox_header"
android:layout_width="wrap_content"
android:saveEnabled="false"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:text="" />
在设备旋转时将复选框状态保持在 RecyclerView 项目中的正确方法是什么?
我的适配器代码:-
public class MusicalAdapter extends RecyclerView.Adapter<MusicalAdapter.ViewHolder> {
private static final String TAG = "MusicalAdapter";
private int previousExpandedPosition = -1;
private int mExpandedPosition = -1;
private final List<MusicalUI> musicals = new ArrayList<>();
private final ItemClickListener clickListener;
MusicalAdapter(final List<MusicalUI> musicals, final ItemClickListener clickListener) {
this.musicals.clear();
this.musicals.addAll(musicals);
//AT THIS POINT THE CHECKED VALUES ARE AS EXPECTED
this.clickListener = clickListener;
}
@NonNull
@Override
public MusicalAdapter.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.all_disco_item_x, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final MusicalAdapter.ViewHolder holder, final int position) {
final MusicalUI musical = musicals.get(position);
Log.d(TAG, "onBindViewHolder() called with: musical = [" + musical.getChecked());
// AT THI SPOINT THE CHECKED VALUES ARE ALWAYS FALSE!!!!!!!!
final boolean isExpanded = position == mExpandedPosition;
for (final MusicalUI musicalx : musicals) {
Log.d(TAG, "onBindViewHolder(): " + musicalx.getChecked());
}
// Header
holder.discoDetailsHeaderLayout.setVisibility(View.VISIBLE);
holder.musicalNameHeader.setText(musical.getDiscoName());
holder.discoSelectedHeader.setOnCheckedChangeListener(null);
holder.discoSelectedHeader.setChecked(musical.getChecked());
holder.discoSelectedHeader.setOnCheckedChangeListener((buttonView, isChecked) -> musical.setChecked(isChecked));
if (isExpanded)
previousExpandedPosition = position;
holder.itemView.setOnClickListener(v -> {
mExpandedPosition = isExpanded ? -1 : position;
notifyItemChanged(previousExpandedPosition);
notifyItemChanged(position);
});
}
@Override
public int getItemCount() {
return musicals.size();
}
MusicalUI getItem(final int position) {
return musicals.get(position);
}
void updateList(final List<MusicalUI> newMusicals) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new GenericDiffUtilCallback<>(this.musicals, newMusicals));
diffResult.dispatchUpdatesTo(this);
this.musicals.clear();
this.musicals.addAll(newMusicals);
}
List<MusicalUI> getList() {
return musicals;
}
/**
*
*/
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
...
}
}
}
它会改变,因为当旋转改变时,你的 activity 会重新启动。您可以编写大量代码来恢复您的状态 或 您可以避免重新启动。这是正确的方法
让你的activity不在轮换中重新启动更改
在 AndroidManifest 中找到您的 activity 并添加
android:configChanges="orientation"
Android 也建议在大多数情况下避免使用它
它说让 android 自己处理配置更改
Caution: Handling the configuration change yourself can make it much
more difficult to use alternative resources, because the system does
not automatically apply them for you. This technique should be
considered a last resort when you must avoid restarts due to a
configuration change and is not recommended for most applications.
To declare that your activity handles a configuration change, edit the
appropriate element in your manifest file to include the
android:configChanges attribute with a value that represents the
configuration you want to handle. Possible values are listed in the
documentation for the android:configChanges attribute. The most
commonly used values are "orientation", "screenSize", and
"keyboardHidden". The "orientation" value prevents restarts when the
screen orientation changes.
我当前的 Android 应用程序包含一个 RecyclerView,列表中的每个项目都有一个复选框。
我的问题是在屏幕旋转时,复选框总是 returns 处于未选中状态。
我将更新的列表项存储在我的活动 ViewModel 中,并将保存的列表传递回 onCreate 中的适配器
从我的调试日志中我可以看到列表状态正确地保存了每个列表项的 checked/unchecked 状态。
我还注销了适配器构造函数中的列表,该列表也显示检查状态仍然有效。
然而,当调用 onBindViewHolder
时,列表项都未选中。
这是我的代码片段:-
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout);
manageViewModel();
recyclerView = findViewById(R.id.my_rv);
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
adapter = new MedicationAdapter(viewModel.getList(), this);
recyclerView.setAdapter(adapter);
}
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
viewModel.saveList(adapter.getList());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
}
我的列表项checkBox在xml中定义如下:-
<android.support.v7.widget.AppCompatCheckBox
android:id="@+id/my_checkbox_header"
android:layout_width="wrap_content"
android:saveEnabled="false"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:text="" />
在设备旋转时将复选框状态保持在 RecyclerView 项目中的正确方法是什么?
我的适配器代码:-
public class MusicalAdapter extends RecyclerView.Adapter<MusicalAdapter.ViewHolder> {
private static final String TAG = "MusicalAdapter";
private int previousExpandedPosition = -1;
private int mExpandedPosition = -1;
private final List<MusicalUI> musicals = new ArrayList<>();
private final ItemClickListener clickListener;
MusicalAdapter(final List<MusicalUI> musicals, final ItemClickListener clickListener) {
this.musicals.clear();
this.musicals.addAll(musicals);
//AT THIS POINT THE CHECKED VALUES ARE AS EXPECTED
this.clickListener = clickListener;
}
@NonNull
@Override
public MusicalAdapter.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.all_disco_item_x, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final MusicalAdapter.ViewHolder holder, final int position) {
final MusicalUI musical = musicals.get(position);
Log.d(TAG, "onBindViewHolder() called with: musical = [" + musical.getChecked());
// AT THI SPOINT THE CHECKED VALUES ARE ALWAYS FALSE!!!!!!!!
final boolean isExpanded = position == mExpandedPosition;
for (final MusicalUI musicalx : musicals) {
Log.d(TAG, "onBindViewHolder(): " + musicalx.getChecked());
}
// Header
holder.discoDetailsHeaderLayout.setVisibility(View.VISIBLE);
holder.musicalNameHeader.setText(musical.getDiscoName());
holder.discoSelectedHeader.setOnCheckedChangeListener(null);
holder.discoSelectedHeader.setChecked(musical.getChecked());
holder.discoSelectedHeader.setOnCheckedChangeListener((buttonView, isChecked) -> musical.setChecked(isChecked));
if (isExpanded)
previousExpandedPosition = position;
holder.itemView.setOnClickListener(v -> {
mExpandedPosition = isExpanded ? -1 : position;
notifyItemChanged(previousExpandedPosition);
notifyItemChanged(position);
});
}
@Override
public int getItemCount() {
return musicals.size();
}
MusicalUI getItem(final int position) {
return musicals.get(position);
}
void updateList(final List<MusicalUI> newMusicals) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new GenericDiffUtilCallback<>(this.musicals, newMusicals));
diffResult.dispatchUpdatesTo(this);
this.musicals.clear();
this.musicals.addAll(newMusicals);
}
List<MusicalUI> getList() {
return musicals;
}
/**
*
*/
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
...
}
}
}
它会改变,因为当旋转改变时,你的 activity 会重新启动。您可以编写大量代码来恢复您的状态 或 您可以避免重新启动。这是正确的方法
让你的activity不在轮换中重新启动更改
在 AndroidManifest 中找到您的 activity 并添加
android:configChanges="orientation"
Android 也建议在大多数情况下避免使用它
它说让 android 自己处理配置更改
Caution: Handling the configuration change yourself can make it much more difficult to use alternative resources, because the system does not automatically apply them for you. This technique should be considered a last resort when you must avoid restarts due to a configuration change and is not recommended for most applications.
To declare that your activity handles a configuration change, edit the appropriate element in your manifest file to include the android:configChanges attribute with a value that represents the configuration you want to handle. Possible values are listed in the documentation for the android:configChanges attribute. The most commonly used values are "orientation", "screenSize", and "keyboardHidden". The "orientation" value prevents restarts when the screen orientation changes.