如何在设备旋转时恢复 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.

More Info