关闭

RecyclerView中使用CheckBox出现勾选混乱的解决方案

标签: RecyclerViewCheckBoxRecyclerView混乱
4627人阅读 评论(1) 收藏 举报
分类:

熟悉RecyclerView的人应该都知道,RecyclerView使用了复用机制,当在RecyclerView中得每一项都添加一个CheckBox时,勾选当前页面的几个CheckBox会发现下面还有其他的CheckBox也被勾选了,今天我们就来讨论一下如何解决这个问题。


首先当然是创建一个项目,然后在activity_main中添加一个RecyclerView控件,当然,在这之前,我们需要先添加RecyclerView的依赖,如下图:





然后 开始编辑activity_main:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <android.support.v7.widget.RecyclerView
        android:id="@+id/id_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
    </android.support.v7.widget.RecyclerView>
</LinearLayout>



接下来为这个RecyclerView创建一个item布局文件,命名为item_recyclerview,并添加一个CheckBox空间,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:gravity="center_vertical">

    <CheckBox
        android:id="@+id/id_check_box"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="CheckBox"/>
</LinearLayout>



接下来要编辑MainActivity了,包括从布局文件中找到刚才的RecyclerView控件,然后为其设置Adapter等,过程不再详细叙述,编辑后的代码如下:

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private MyAdapter myAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view);
        myAdapter = new MyAdapter();
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(myAdapter);
    }



    private class MyAdapter extends RecyclerView.Adapter {

        private List<String> content;

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);
            return new MyViewHolder(view);
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            MyViewHolder myViewHolder = (MyViewHolder) holder;
            myViewHolder.checkBox.setText(content.get(position));

        }

        @Override
        public int getItemCount() {
            content = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                content.add("CheckBox" + i);
            }
            return content.size();
        }
    }


    private class MyViewHolder extends RecyclerView.ViewHolder {

        private CheckBox checkBox;

        public MyViewHolder(View itemView) {
            super(itemView);
            checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);
        }
    }
}


可以看到,我们为这个RecyclerView设置了100个item,每个item里面都含有一个CheckBox,这时候运行这个应用,勾选出现的屏幕上的某一个或者多个CheckBox之后,当你向下拉的时候,问题出现了,你会发现下面会有很多的CheckBox也被选中了。下面我们就来着手解决这个问题,其实要解决也很简单,可以定义一个boolean类型的数组或者列表,用它来控制CheckBox的选中状态,当某个CheckBox被选中的时候将其选中状态记录在数组或列表中,当某个CheckBox滚动到屏幕上的时候,再用数组或列表中对应的值把它的选中状态改回来就好了,修改后的代码如下:

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private MyAdapter myAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view);
        myAdapter = new MyAdapter();
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(myAdapter);
    }



    private class MyAdapter extends RecyclerView.Adapter {

        private List<String> content;
        private boolean[] flag = new boolean[100];//此处添加一个boolean类型的数组

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);
            return new MyViewHolder(view);
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
            MyViewHolder myViewHolder = (MyViewHolder) holder;
            myViewHolder.checkBox.setText(content.get(position));
            
            myViewHolder.checkBox.setOnCheckedChangeListener(null);//先设置一次CheckBox的选中监听器,传入参数null
            myViewHolder.checkBox.setChecked(flag[position]);//用数组中的值设置CheckBox的选中状态
            
            //再设置一次CheckBox的选中监听器,当CheckBox的选中状态发生改变时,把改变后的状态储存在数组中
            myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                    flag[position] = b;
                }
            });
        }

        @Override
        public int getItemCount() {
            content = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                content.add("CheckBox" + i);
            }
            return content.size();
        }
    }


    private class MyViewHolder extends RecyclerView.ViewHolder {

        private CheckBox checkBox;

        public MyViewHolder(View itemView) {
            super(itemView);
            checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);
        }
    }
}


比较这两段代码,我们会发现,首先我们定义了一个长度为100的数组,然后设置CheckBox的选中监听器,把null作为参数传进去,然后用数组中的值设置对应CheckBox的选中状态,最后再一次设置CheckBox的选中监听器,把CheckBox的选中状态储存在数组中的相应位置中。再次运行,发现问题已解决。






下面我们来讨论一下,如果要在RecyclerView的外面再添加一个CheckBox,用外面的CheckBox来控制RecyclerView中的CheckBox的全选和取消全选,要如何实现呢?


其实也很简单,只要用这个CheckBox来控制之前所定义的数组的指就好了。


首先来修改一下activity_main,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:gravity="center_vertical">
        <CheckBox
            android:id="@+id/id_select_all"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:text="SelectAll"/>
    </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/id_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v7.widget.RecyclerView>
</LinearLayout>


我们在RecyclerView的外面添加了一个CheckBox,用这个CheckBox来控制RecyclerView中的CheckBox的全选,接下来修改MainActivity:

public class MainActivity extends AppCompatActivity {

    private CheckBox selectAll;
    private RecyclerView recyclerView;
    private MyAdapter myAdapter;
    private boolean []flag;//把flag数组定义为全局变量

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        selectAll = (CheckBox) findViewById(R.id.id_select_all);
        recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view);
        flag = new boolean[100];//初始化flag
        myAdapter = new MyAdapter();
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(myAdapter);

        //设置外面CheckBox的选中监听器,把它的选中状态赋值给其他的所有CheckBox,然后更新RecyclerView的Adapter
        selectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                for (int i = 0; i < 100; i++) {
                    flag[i] = b;
                }
                myAdapter.notifyDataSetChanged();
            }
        });
    }



    private class MyAdapter extends RecyclerView.Adapter {

        private List<String> content;
//        private boolean[] flag = new boolean[100];

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);
            return new MyViewHolder(view);
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
            MyViewHolder myViewHolder = (MyViewHolder) holder;
            myViewHolder.checkBox.setText(content.get(position));
            myViewHolder.checkBox.setOnCheckedChangeListener(null);
            myViewHolder.checkBox.setChecked(flag[position]);
            myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                    flag[position] = b;
                }
            });


        }

        @Override
        public int getItemCount() {
            content = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                content.add("CheckBox" + i);
            }
            return content.size();
        }
    }


    private class MyViewHolder extends RecyclerView.ViewHolder {

        private CheckBox checkBox;

        public MyViewHolder(View itemView) {
            super(itemView);
            checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);
        }
    }
}


这里我们先把记录CheckBox选中状态的数组定义为全局变量,然后设置外面的CheckBox的监听器,把它的选中状态赋值给其他的所有CheckBox,紧接着更新一下RecyclerView的Adapter就可以了




这里我们在讨论一下RecyclerView的另外一个问题,就是当要删除某个子项的时候会出现删除紊乱的情况,为了说明这个问题,我们先来尝试实践一下,修改item_recyclerview:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:gravity="center_vertical">

    <CheckBox
        android:id="@+id/id_check_box"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:text="CheckBox"/>
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_marginRight="10dp"
        android:text="delete"/>
</RelativeLayout>

我们增加了一个Button,接下来设置这个Button,当它被点击的时候就删除它所在位置的item,修改MainActivity如下,主要修改的是Adapter部分,其他部分的代码就不贴了:

    private class MyAdapter extends RecyclerView.Adapter {

//        private List<String> content;
//        private boolean[] flag = new boolean[100];

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false);
            return new MyViewHolder(view);
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
            final MyViewHolder myViewHolder = (MyViewHolder) holder;
            myViewHolder.checkBox.setText(content.get(position));
            myViewHolder.checkBox.setOnCheckedChangeListener(null);
            myViewHolder.checkBox.setChecked(flag[position]);
            myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                    flag[position] = b;
                }
            });
            //设置监听器,当按钮被点击是,删除它所在的item
            myViewHolder.button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    content.remove(position);
                    notifyItemRemoved(position);
                }
            });


        }

        @Override
        public int getItemCount() {
//            content = new ArrayList<>();
//            for (int i = 0; i < 100; i++) {
//                content.add("CheckBox" + i);
//            }
            return content.size();
        }
    }


    private class MyViewHolder extends RecyclerView.ViewHolder {

        private CheckBox checkBox;
        private Button button;//定义删除按钮

        public MyViewHolder(View itemView) {
            super(itemView);
            checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box);
            button = (Button) itemView.findViewById(R.id.id_delete);
        }
    }

这是点击Button,我们会发现,问题出现了,第一次可以正常删除,第二次删除的item却是我们点击的Button所在的下一个item,后面的删除也会各种混乱,这是因为函数里面的传入的参数position,它是在进行onBind操作时确定的,在删除单项后,已经出现在画面里的项不会再有调用onBind机会,这样它保留的position一直是未进行删除操作前的postion值,对于尚未进入画面的单项来说,它会使用新的position值(好吧这段是抄的,其实我也不太懂啥意思),解决方法如下:

myViewHolder.button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        content.remove(position);
        notifyItemRemoved(position);
        notifyItemRangeChanged(position, content.size());//对于被删掉的位置及其后range大小范围内的view进行重新onBindViewHolder
    }
});

只要加一行代码就好了,这行代码的作用就是对于被删掉的位置及其后range大小范围内的view进行重新onBindViewHolder



此项目已上传到githut:点击打开链接





1
3
查看评论

Android高级控件之RecyclerView上放置CheckBox

列表上放置单选框,实现多选功能。本文精髓都在适配器当中,做了很多的判断要仔细看清楚哦!对RecyclerView还不熟悉的可以查看一下这篇—Android RecyclerView自定义点击事件和长按事件1.我们先来看看实现的效果吧,长按可以将checkBox显示或隐藏,点击确定可以获取到你选中的条...
  • a_zhon
  • a_zhon
  • 2016-10-26 00:05
  • 6873

RecyclerView中使用checkbox遇到的问题

今天用recyclerview时候发现checkbox重复使用会导致OnCheckedChangeListener 出现一个不太友好的问题,问题是这样的: Recyclerview 会回收使用组件,回收checkbox的时候会触发OnCheckedChangeListener 事件,导致我们写的li...
  • nerver87
  • nerver87
  • 2016-05-01 19:18
  • 5149

Android RecyclerView中实现ChecckBox单选的正确方式

以前使用recyclerView时,如果item中有单选框需要选中,一般的实现方式是创建一个boolean类型的ArrayList用来存储列表中每个item的checkBox是否选中,设置一个标志位mCurrentPosition来记录当前被选中的checkBox的item的position,然后在...
  • qq402164452
  • qq402164452
  • 2016-12-05 12:04
  • 4107

Recyclerview+checkbox

  • 2017-06-18 15:25
  • 22.26MB
  • 下载

Android 基于RecyclerView实现批量操作

不知道大家对淘宝,JD的App购物车有木有印象,购物车中的多个商品可以批量删除,支付,分享等等。。先来看张图,该图来自水友截图: 上图来自淘宝购物车界面。可以看到,我们对商品可以进行品量删除,移到收藏夹,分享功能。ok,今天我们就用RecyclerView来实现类似批量更新的操作。 首先,我们来...
  • u013718120
  • u013718120
  • 2016-09-07 21:15
  • 6971

RecyclerView+CheckBox实现列表数据选择操作

这些时间做安卓盒子项目,因为安卓电视的显示器比较大,所以一个界面显示 很多数据 ,最多的时候,一个Actvity中用到了好几个RecyclerView。 在RecyclerView中实现Item选中处理时,发现用CheckBox的OnCheckedChangeListener监听事件时,会达不到...
  • true100
  • true100
  • 2017-06-20 14:19
  • 1432

RecyclerView+Checkbox乱序问题

前几天做项目时用到了Android5.0的新特性RecyclerVIew+Checkbox,乱序问题耽误了好久,终于解决了,在此分享一下我做的项目中不仅要避免Checkbox乱序问题,还要将所选中的id的电话号码传递给Activity,说一下大概思路吧、 我在adapter中做了如下工作: 第一...
  • zrf1335348191
  • zrf1335348191
  • 2015-11-23 13:32
  • 6462

RecyclerView 复用出现 CheckBox 或者 设置背景混乱问题

RecyclerView 复用出现 CheckBox 或者 设置背景混乱问题 由于RecyclerView 与ListView 都使用了复用技术,所以会出现滚动后出现CheckBox选中混乱或者是Item的背景混乱问题。 解决办法: 1.在onBindViewHolder(...) 方...
  • u011164565
  • u011164565
  • 2016-08-02 18:09
  • 2340

RecyclerView中CheckBox显示混乱的解决办法

前言:最近加入了一个团队。独立开发Android端,这对我来说是一个很大的挑战,再以前开发,总有老手带我飙车,很多技术难点和一些第三方工具我都没有接触,现在我必须独自面对这些,但即使是挑战,那就努力面对,慢慢克服。现在这个团队氛围不错,大家都平平等等为了项目的发展默默奉献,没有内耗。大家心中就只有一...
  • VickyWinner
  • VickyWinner
  • 2016-11-12 11:33
  • 912

RecyclerView+checkbox滑动导致复用混乱问题的解决方案

最近做一个项目,里面遇到了一个小问题吧我觉得这些问题也是我们时常会用到的。我们需要在用户新注册的时候为他推荐一些人来选择关注。用到了RecyclerView 那么在RecyclerView的Adapter里面用到的是checkbox让用户有选择的进行关注,当列表足够多的时候这时下拉就会产生check...
  • qq_16265959
  • qq_16265959
  • 2016-11-29 20:28
  • 1453
    个人资料
    • 访问:28584次
    • 积分:584
    • 等级:
    • 排名:千里之外
    • 原创:30篇
    • 转载:2篇
    • 译文:0篇
    • 评论:5条
    文章分类
    最新评论