1.需求前言
需要做一个勾选列表,提供不同的选项给用户进行勾选,并将结果保存在本地
效果图:
image.png
2.实现思路
采用Recycleview+Checkbox的方式 每个recycleview的每个item就是一个选项,可以勾选,再次点击取消勾选
进入勾选页面时,先读取本地的设置,每个item设置是否已经勾选
难点:recycleview的holder复用导致勾选混乱
3.实现
3.1 布局
这里采用极简的方式快速实现,所以布局非常简单,xml文件就不贴出来了
这是item的布局,我使用了RelativeLayout,但其实可以使用LinearLayout
左边一个TextView 右边一个CheckBox
item.png
然后activity的布局就一个RecyclerView就可以
1 2 3 4 5 6 | <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView_product_type" android:layout_width="match_parent" android:layout_height="match_parent" tools:listitem="@layout/item_preference_product" /> |
3.2 Activity和Adapter的代码实现
我尽量注释清楚,以下代码是从项目中摘取出来的代码,保留灵魂,舍弃整体。不能直接使用,但是如果读懂了代码的话,是可以很轻松的自己实现的
3.2.1 Adapter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | public class ProdectTypeAdapter extends DefaultAdapter<ProductTypeBean> { /**Set<String>是用于保存已经勾选的商品列表 * SharedPreferences能保存的列表只能是Set<String>类型 * 所以这里采用Set<String>来记录勾选的商品列表 * SharedPreferences需要保存更复杂的bean对象的话可以转换为JSON的字符串来保存 * */ //mProductPreferences之前已经勾选的商品列表 在打开界面时我们需要默认勾选上 Set<String> mProductPreferences; //ischoose 商品名称以及对应是否已经勾选 Map<String, Boolean> ischoose = new HashMap<>(); public ProdectTypeAdapter(List<ProductTypeBean> infos, Set<String> mProductPreferences) { super(infos); this.mProductPreferences = mProductPreferences; } @Override public BaseHolder<ProductTypeBean> getHolder(View v, int viewType) { //这里根据所有的商品名称和mProductPreferences作比较 //如果mProductPreferences存在则已勾选 否则不勾选 比较结果存入ischoose for (ProductTypeBean productTypeBean : getInfos()) { if (mProductPreferences.contains(productTypeBean.getProductTypeId())) { ischoose.put(productTypeBean.getProductTypeId(), true); } else { ischoose.put(productTypeBean.getProductTypeId(), false); } } ProdectTypeHolder holder = new ProdectTypeHolder(v, ischoose); //!!!这句代码非常关键 这里禁止holder的复用 直接杜绝了勾选混淆的可能性 //代价是如果选项特别巨大,会导致recycleview滑动卡顿 但一般是不会有太多选项的 holder.setIsRecyclable(false); return holder; } @Override public int getLayoutId(int viewType) { return R.layout.item_preference_product; } //holder中填充每个item的数据 class ProdectTypeHolder extends BaseHolder<ProductTypeBean> { @BindView(R.id.tv_product_name) TextView productName;//显示每个商品名称 @BindView(R.id.checkbox_product) CheckBox checkbox;//每个商品的选择框 Map<String, Boolean> ischoose; public ProdectTypeHolder(View itemView, Map<String, Boolean> ischoose) { super(itemView); this.ischoose = ischoose; } //setData填充每个item的数据 @Override public void setData(ProductTypeBean data, int position) { //显示商品名称 productName.setText(data.getVatRatio() + "%" + itemView.getContext().getString(data.getDescRes())); //刚开始启动 勾选框是否默认已勾选(根据ischoose) checkbox.setChecked(ischoose.get(data.getProductTypeId())); //我们不需要勾选框的监听 所以设置不可点击 checkbox.setClickable(false); } } } |
3.2.2 Activity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | public class ProductTypePreferenceSettingActivity extends BaseActivity { //SharedPreferences存储对象 用于将选择结果保存到本地内存 @Inject SharedPreferences mSharedPreferences; @BindView(R.id.recyclerView_product_type) RecyclerView recyclerView; ProdectTypeAdapter mAdapter;//recycleview适配器 LinearLayoutManager mManager;//recycleview的布局管理器 用于单独获取item中的checkbox对象 List<ProductTypeBean> allProductType;//所有的可以勾选的商品列表 /**Set<String>是用于保存已经勾选的商品列表 * SharedPreferences能保存的列表只能是Set<String>类型 * 所以这里采用Set<String>来记录勾选的商品列表 * SharedPreferences需要保存更复杂的bean对象的话可以转换为JSON的字符串来保存 * */ Set<String> mProductPreferences = new HashSet<>();//之前已经勾选了并保存在本地的商品 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_product_type_preference_setting); //所有的可以勾选的商品列表 allProductType = Constants.allProductTypeList; //读取本地保存的已经勾选的商品 mProductPreferences = mSharedPreferences.getStringSet(Constants.PRODUCTPREFERENCES, mProductPreferences); //实例化适配器 mAdapter = new ProdectTypeAdapter(allProductType, mProductPreferences); //实例化布局管理器 这里是线性布局 mManager = new LinearLayoutManager(this); //设置布局 recyclerView.setLayoutManager(mManager); //设置适配器 recyclerView.setAdapter(mAdapter); //设置recycleview中每个item的监听 mAdapter.setOnItemClickListener(new DefaultAdapter.OnRecyclerViewItemClickListener() { @Override public void onItemClick(View view, int viewType, Object data, int position) { //根据点击的位置获取到item的布局文件 View itemview = mManager.findViewByPosition(position); LinearLayout layout = (LinearLayout) itemview; //获取到每个item中的checkbox对象 CheckBox checkbox = (CheckBox) layout.findViewById(R.id.checkbox_product); //根据点击的位置读取到商品对象 ProductTypeBean product = allProductType.get(position); //进一步读取到商品的id String productId = product.getProductTypeId(); //如果这个商品已经勾选了 设置checkbox为未勾选 从mProductPreferences移除 //反之 将checkbox设置为已勾选 加入mProductPreferences if (mProductPreferences.contains(productId)) { mProductPreferences.remove(productId); checkbox.setChecked(false); } else { mProductPreferences.add(productId); checkbox.setChecked(true); } } }); } @Override protected void onDestroy() { //退出时 进行保存写入 mSharedPreferences.edit().putStringSet(Constants.PRODUCTPREFERENCES, mProductPreferences).commit(); setResult(RESULT_OK); super.onDestroy(); } } |
到这里我们就基本上完成了多选列表,再次强调,以上代码不可直接使用,但是注释写的非常清楚,可以看懂了,根据自己的业务需求来实现。
这只是最简单的多选框,如果要加入一键全选 ,一键取消 ,单选,反选等等,可以自己扩展
4.难点总结
难点1:holder的复用导致勾选混乱 比如我勾了第1个商品,但是第11个也会勾上,这个可以靠单独的数据源来解决,比如代码中的ischoose(会有其他问题,比如勾选了,滑动导致 holder刷新,勾选状态又没了),当然也可以简单除暴
难点2:获取单个item中的单个view,这里的view是checkbox。通过布局管理器来获取到view对象,跟着代码里来做就可以