Android中RecyclerView+CheckBox的使用
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再次启用时,会根据最新的数据自动确定状态。