Android中RecyclerView+CheckBox的使用

龙旋

共 5980字,需浏览 12分钟

 ·

2021-11-08 11:40

1、需求前言


需要做一个勾选列表,提供不同的选项给用户进行勾选,并将结果保存在本地

效果图:


2、实现思路


采用Recycleview+Checkbox的方式 每个recycleview的每个item就是一个选项,可以勾选,再次点击取消勾选


进入勾选页面时,先读取本地的设置,每个item设置是否已经勾选


难点:recycleview的holder复用导致勾选混乱


3、实现


3.1 布局


这里采用极简的方式快速实现,所以布局非常简单,xml文件就不贴出来了
这是item的布局,我使用了RelativeLayout,但其实可以使用LinearLayout
左边一个TextView 右边一个CheckBox



然后activity的布局就一个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

public class ProdectTypeAdapter extends DefaultAdapter {
/**Set是用于保存已经勾选的商品列表 * SharedPreferences能保存的列表只能是Set类型 * 所以这里采用Set来记录勾选的商品列表 * SharedPreferences需要保存更复杂的bean对象的话可以转换为JSON的字符串来保存 * */ //mProductPreferences之前已经勾选的商品列表 在打开界面时我们需要默认勾选上 Set mProductPreferences;
//ischoose 商品名称以及对应是否已经勾选 Map ischoose = new HashMap<>();
public ProdectTypeAdapter(List infos, Set mProductPreferences) { super(infos); this.mProductPreferences = mProductPreferences; }

@Override public BaseHolder 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 {
@BindView(R.id.tv_product_name) TextView productName;//显示每个商品名称 @BindView(R.id.checkbox_product) CheckBox checkbox;//每个商品的选择框 Map ischoose;

public ProdectTypeHolder(View itemView, Map 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

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 allProductType;//所有的可以勾选的商品列表
/**Set是用于保存已经勾选的商品列表 * SharedPreferences能保存的列表只能是Set类型 * 所以这里采用Set来记录勾选的商品列表 * SharedPreferences需要保存更复杂的bean对象的话可以转换为JSON的字符串来保存 * */ Set 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刷新,勾选状态又没了),当然也可以简单除暴holder.setIsRecyclable(false);禁止holder的复用。


难点2:获取单个item中的单个view,这里的view是checkbox。通过布局管理器来获取到view对象,跟着代码里来做就可以


!!!难点3:一定要将checkbox的初始状态传入adapter,否者虽然取消了复用,但是由于recycleview会自动回收,导致选择了第一个,下滑在上滑回来时,勾选状态却是“未勾选”。
这里的checkbox就是根据mProductPreferences来确定的。


个人猜想:recycleview在回收了holder再次启用时,会根据最新的数据自动确定状态。

浏览 111
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报