Android中RecyclerView+CheckBox的使用
1、需求前言
需要做一個(gè)勾選列表,提供不同的選項(xiàng)給用戶進(jìn)行勾選,并將結(jié)果保存在本地
效果圖:

2、實(shí)現(xiàn)思路
采用Recycleview+Checkbox的方式 每個(gè)recycleview的每個(gè)item就是一個(gè)選項(xiàng),可以勾選,再次點(diǎn)擊取消勾選
進(jìn)入勾選頁(yè)面時(shí),先讀取本地的設(shè)置,每個(gè)item設(shè)置是否已經(jīng)勾選
難點(diǎn):recycleview的holder復(fù)用導(dǎo)致勾選混亂
3、實(shí)現(xiàn)
3.1 布局
這里采用極簡(jiǎn)的方式快速實(shí)現(xiàn),所以布局非常簡(jiǎn)單,xml文件就不貼出來(lái)了
這是item的布局,我使用了RelativeLayout,但其實(shí)可以使用LinearLayout
左邊一個(gè)TextView 右邊一個(gè)CheckBox

然后activity的布局就一個(gè)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的代碼實(shí)現(xiàn)
我盡量注釋清楚,以下代碼是從項(xiàng)目中摘取出來(lái)的代碼,保留靈魂,舍棄整體。不能直接使用,但是如果讀懂了代碼的話,是可以很輕松的自己實(shí)現(xiàn)的。
3.2.1 Adapter
public class ProdectTypeAdapter extends DefaultAdapter{ /**Set是用于保存已經(jīng)勾選的商品列表 * SharedPreferences能保存的列表只能是Set類型 * 所以這里采用Set來(lái)記錄勾選的商品列表 * SharedPreferences需要保存更復(fù)雜的bean對(duì)象的話可以轉(zhuǎn)換為JSON的字符串來(lái)保存* *///mProductPreferences之前已經(jīng)勾選的商品列表 在打開(kāi)界面時(shí)我們需要默認(rèn)勾選上SetmProductPreferences; //ischoose 商品名稱以及對(duì)應(yīng)是否已經(jīng)勾選Mapischoose = new HashMap<>(); public ProdectTypeAdapter(Listinfos, Set mProductPreferences) { super(infos);this.mProductPreferences = mProductPreferences;}@Overridepublic BaseHoldergetHolder(View v, int viewType) { //這里根據(jù)所有的商品名稱和mProductPreferences作比較//如果mProductPreferences存在則已勾選 否則不勾選 比較結(jié)果存入ischoosefor (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);//?。?!這句代碼非常關(guān)鍵 這里禁止holder的復(fù)用 直接杜絕了勾選混淆的可能性//代價(jià)是如果選項(xiàng)特別巨大,會(huì)導(dǎo)致recycleview滑動(dòng)卡頓 但一般是不會(huì)有太多選項(xiàng)的holder.setIsRecyclable(false);return holder;}@Overridepublic int getLayoutId(int viewType) {return R.layout.item_preference_product;}//holder中填充每個(gè)item的數(shù)據(jù)class ProdectTypeHolder extends BaseHolder{ @BindView(R.id.tv_product_name)TextView productName;//顯示每個(gè)商品名稱@BindView(R.id.checkbox_product)CheckBox checkbox;//每個(gè)商品的選擇框Mapischoose; public ProdectTypeHolder(View itemView, Mapischoose) { super(itemView);this.ischoose = ischoose;}//setData填充每個(gè)item的數(shù)據(jù)@Overridepublic void setData(ProductTypeBean data, int position) {//顯示商品名稱productName.setText(data.getVatRatio() + "%" + itemView.getContext().getString(data.getDescRes()));//剛開(kāi)始啟動(dòng) 勾選框是否默認(rèn)已勾選(根據(jù)ischoose)checkbox.setChecked(ischoose.get(data.getProductTypeId()));//我們不需要勾選框的監(jiān)聽(tīng) 所以設(shè)置不可點(diǎn)擊checkbox.setClickable(false);}}}
3.2.2 Activity
public class ProductTypePreferenceSettingActivity extends BaseActivity {//SharedPreferences存儲(chǔ)對(duì)象 用于將選擇結(jié)果保存到本地內(nèi)存@InjectSharedPreferences mSharedPreferences;@BindView(R.id.recyclerView_product_type)RecyclerView recyclerView;ProdectTypeAdapter mAdapter;//recycleview適配器LinearLayoutManager mManager;//recycleview的布局管理器 用于單獨(dú)獲取item中的checkbox對(duì)象ListallProductType;//所有的可以勾選的商品列表 /**Set是用于保存已經(jīng)勾選的商品列表 * SharedPreferences能保存的列表只能是Set類型 * 所以這里采用Set來(lái)記錄勾選的商品列表 * SharedPreferences需要保存更復(fù)雜的bean對(duì)象的話可以轉(zhuǎn)換為JSON的字符串來(lái)保存* */SetmProductPreferences = new HashSet<>();//之前已經(jīng)勾選了并保存在本地的商品 @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_product_type_preference_setting);//所有的可以勾選的商品列表allProductType = Constants.allProductTypeList;//讀取本地保存的已經(jīng)勾選的商品mProductPreferences = mSharedPreferences.getStringSet(Constants.PRODUCTPREFERENCES, mProductPreferences);//實(shí)例化適配器mAdapter = new ProdectTypeAdapter(allProductType, mProductPreferences);//實(shí)例化布局管理器 這里是線性布局mManager = new LinearLayoutManager(this);//設(shè)置布局recyclerView.setLayoutManager(mManager);//設(shè)置適配器recyclerView.setAdapter(mAdapter);//設(shè)置recycleview中每個(gè)item的監(jiān)聽(tīng)mAdapter.setOnItemClickListener(new DefaultAdapter.OnRecyclerViewItemClickListener() {@Overridepublic void onItemClick(View view, int viewType, Object data, int position) {//根據(jù)點(diǎn)擊的位置獲取到item的布局文件View itemview = mManager.findViewByPosition(position);LinearLayout layout = (LinearLayout) itemview;//獲取到每個(gè)item中的checkbox對(duì)象CheckBox checkbox = (CheckBox) layout.findViewById(R.id.checkbox_product);//根據(jù)點(diǎn)擊的位置讀取到商品對(duì)象ProductTypeBean product = allProductType.get(position);//進(jìn)一步讀取到商品的idString productId = product.getProductTypeId();//如果這個(gè)商品已經(jīng)勾選了 設(shè)置checkbox為未勾選 從mProductPreferences移除//反之 將checkbox設(shè)置為已勾選 加入mProductPreferencesif (mProductPreferences.contains(productId)) {mProductPreferences.remove(productId);checkbox.setChecked(false);} else {mProductPreferences.add(productId);checkbox.setChecked(true);}}});}@Overrideprotected void onDestroy() {//退出時(shí) 進(jìn)行保存寫(xiě)入mSharedPreferences.edit().putStringSet(Constants.PRODUCTPREFERENCES, mProductPreferences).commit();setResult(RESULT_OK);super.onDestroy();}}
到這里我們就基本上完成了多選列表,再次強(qiáng)調(diào),以上代碼不可直接使用,但是注釋寫(xiě)的非常清楚,可以看懂了,根據(jù)自己的業(yè)務(wù)需求來(lái)實(shí)現(xiàn)。
這只是最簡(jiǎn)單的多選框,如果要加入一鍵全選 ,一鍵取消 ,單選,反選等等,可以自己擴(kuò)展。
4、難點(diǎn)總結(jié)
難點(diǎn)1:holder的復(fù)用導(dǎo)致勾選混亂 比如我勾了第1個(gè)商品,但是第11個(gè)也會(huì)勾上,這個(gè)可以靠單獨(dú)的數(shù)據(jù)源來(lái)解決,比如代碼中的ischoose(會(huì)有其他問(wèn)題,比如勾選了,滑動(dòng)導(dǎo)致 holder刷新,勾選狀態(tài)又沒(méi)了),當(dāng)然也可以簡(jiǎn)單除暴holder.setIsRecyclable(false);禁止holder的復(fù)用。
難點(diǎn)2:獲取單個(gè)item中的單個(gè)view,這里的view是checkbox。通過(guò)布局管理器來(lái)獲取到view對(duì)象,跟著代碼里來(lái)做就可以
?。?!難點(diǎn)3:一定要將checkbox的初始狀態(tài)傳入adapter,否者雖然取消了復(fù)用,但是由于recycleview會(huì)自動(dòng)回收,導(dǎo)致選擇了第一個(gè),下滑在上滑回來(lái)時(shí),勾選狀態(tài)卻是“未勾選”。
這里的checkbox就是根據(jù)mProductPreferences來(lái)確定的。
個(gè)人猜想:recycleview在回收了holder再次啟用時(shí),會(huì)根據(jù)最新的數(shù)據(jù)自動(dòng)確定狀態(tài)。
