Android如何保存ListView CHOICE_MODE_MULTIPLE的复选框勾选状态?
嘿,我来帮你搞定ListView多选状态保存的问题~先看你代码里的几个小问题:你自定义了FM1Adapter但没用到,反而用了ArrayAdapter;还有mListView.setItemChecked()没传参数会报错;另外getCheckedItemPositions()调用时机不对,得在要保存状态的时候用才行。
下面分两种情况给你解决方案:
方法一:用系统自带的多选布局(最简单)
你代码里提到了android.R.layout.simple_list_item_multiple_choice,这个布局自带CheckBox,而且ListView的CHOICE_MODE_MULTIPLE会帮我们管理选中状态,不用自己处理CheckBox逻辑。
步骤1:保存选中状态
在Fragment的onSaveInstanceState方法里保存选中的位置,这样横竖屏切换或者Fragment重建时能恢复:
@Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // 获取所有选中项的状态 SparseBooleanArray checkedPositions = mListView.getCheckedItemPositions(); ArrayList<Integer> selectedItems = new ArrayList<>(); // 把选中的位置转成ArrayList(Bundle能直接存这个类型) for (int i = 0; i < checkedPositions.size(); i++) { int position = checkedPositions.keyAt(i); if (checkedPositions.valueAt(i)) { selectedItems.add(position); } } outState.putIntegerArrayList("selected_check_items", selectedItems); }
步骤2:恢复选中状态
在你设置好Adapter之后,读取之前保存的状态,逐个设置item为选中:
@Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_check_tab1,container,false); mListView = rootView.findViewById(R.id.check_ListView1); mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); // 初始化Adapter ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_multiple_choice, air_list); mListView.setAdapter(adapter); // 恢复选中状态 if (savedInstanceState != null) { ArrayList<Integer> selectedItems = savedInstanceState.getIntegerArrayList("selected_check_items"); if (selectedItems != null) { for (int position : selectedItems) { // 注意position不能超出列表长度 if (position < air_list.length) { mListView.setItemChecked(position, true); } } } } return rootView; }
方法二:自定义Adapter(如果需要自定义布局)
如果你一定要用自己的布局(比如你代码里的FM1Adapter),得自己管理选中状态,同时和ListView的状态同步,避免冲突。
步骤1:自定义item布局
先写一个包含TextView和CheckBox的布局item_check.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:padding="16dp"> <CheckBox android:id="@+id/item_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@+id/item_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30sp" android:textColor="@android:color/black" android:layout_marginStart="16dp"/> </LinearLayout>
步骤2:修改自定义Adapter
让Adapter自己维护选中状态,同时和ListView联动:
class FM1Adapter extends BaseAdapter{ private SparseBooleanArray mCheckedStates; private ListView mListView; // 构造函数:传入之前保存的选中状态和ListView实例 public FM1Adapter(SparseBooleanArray savedStates, ListView listView) { mCheckedStates = savedStates != null ? savedStates : new SparseBooleanArray(); mListView = listView; } @Override public int getCount() { return air_list.length; } @Override public Object getItem(int position) { return air_list[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_check, parent, false); holder = new ViewHolder(); holder.textView = convertView.findViewById(R.id.item_text); holder.checkBox = convertView.findViewById(R.id.item_checkbox); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.textView.setText(air_list[position]); // 设置CheckBox的选中状态 holder.checkBox.setChecked(mCheckedStates.get(position, false)); // 监听CheckBox点击,更新状态并同步到ListView holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mCheckedStates.put(position, isChecked); mListView.setItemChecked(position, isChecked); } }); return convertView; } // 提供方法获取当前选中状态,方便保存 public SparseBooleanArray getCheckedStates() { return mCheckedStates; } // ViewHolder优化性能 static class ViewHolder { TextView textView; CheckBox checkBox; } }
步骤3:保存和恢复状态
在Fragment里保存和恢复:
@Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_check_tab1,container,false); mListView = rootView.findViewById(R.id.check_ListView1); mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); // 读取保存的状态 SparseBooleanArray savedStates = null; if (savedInstanceState != null) { // 把Bundle里的ArrayList转成SparseBooleanArray ArrayList<Integer> selectedItems = savedInstanceState.getIntegerArrayList("selected_check_items"); if (selectedItems != null) { savedStates = new SparseBooleanArray(); for (int position : selectedItems) { savedStates.put(position, true); } } } // 初始化自定义Adapter FM1Adapter adapter = new FM1Adapter(savedStates, mListView); mListView.setAdapter(adapter); return rootView; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // 获取Adapter里的选中状态 FM1Adapter adapter = (FM1Adapter) mListView.getAdapter(); SparseBooleanArray checkedStates = adapter.getCheckedStates(); ArrayList<Integer> selectedItems = new ArrayList<>(); for (int i = 0; i < checkedStates.size(); i++) { int position = checkedStates.keyAt(i); if (checkedStates.valueAt(i)) { selectedItems.add(position); } } outState.putIntegerArrayList("selected_check_items", selectedItems); }
小提醒:如果你想长期保存(比如退出APP再打开也能恢复),可以把选中状态存到SharedPreferences里,原理和存Bundle一样,只是把ArrayList转成字符串存储,读取时再转回来就行。
内容的提问来源于stack exchange,提问作者Hyojun Park

