RecyclerView+CheckBox的使用

1.需求前言

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


image.png

2.实现思路

采用Recycleview+Checkbox的方式 每个recycleview的每个item就是一个选项,可以勾选,再次点击取消勾选
进入勾选页面时,先读取本地的设置,每个item设置是否已经勾选
难点:recycleview的holder复用导致勾选混乱

3.实现

3.1 布局

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


item.png

然后activity的布局就一个RecyclerView就可以

    <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

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

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刷新,勾选状态又没了),当然也可以简单除暴holder.setIsRecyclable(false);禁止holder的复用。
难点2:获取单个item中的单个view,这里的view是checkbox。通过布局管理器来获取到view对象,跟着代码里来做就可以

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容