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

熟悉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:点击打开链接

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • checkbox勾选判断代码分析

    复制代码 代码如下: var xieYi=document.getElementById("xieYi");if(!xieYi.checked){    alert("请先阅读并勾选注册协议!");    return;     } 最开始这样写,不过并不是所有的情况都需要勾选这个协议,协议有时不会在前台页面显示,所以改了第二种 复制代码 代码如下: var xieYi=document.getElementById("xieYi");    i

  • checkbox设置复选框的只读效果不让用户勾选

    在Web开发中,有时候需要显示一些复选框(checkbox),表明这个地方是可以进行勾选操作的,但是有时候是只想告知用户"这个地方是可以进行勾选操作的"而不想让用户在此处勾选(比如在信息展示页面),这时候就需要将复选框设置成只读的效果. 提到只读,很容易想到使用readonly属性,但是对于复选框来说,这个属性和期望得到的效果是有差别的.原因在于readonly属性关联的是页面元素的value属性(例如textbox,设置了readonly就不能修改输入框的文本内容),而复选框的勾选/

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

    熟悉RecyclerView的人应该都知道,RecyclerView使用了复用机制,当在RecyclerView中得每一项都添加一个CheckBox时,勾选当前页面的几个CheckBox会发现下面还有其他的CheckBox也被勾选了,今天我们就来讨论一下如何解决这个问题. 首先当然是创建一个项目,然后在activity_main中添加一个RecyclerView控件,当然,在这之前,我们需要先添加RecyclerView的依赖,如下图: 然后 开始编辑activity_main: <?xml v

  • jquery checkbox无法用attr()二次勾选问题的解决方法

    今晨,漂亮的测试妹妹提了个奇怪的bug,说我一功能checkbox时隐时现,比如第一次打开有勾选,第n次打开可能就不选了. 想到与美女有亲密接触机会,马上鸡动起来. 经过偶层层抽次剥茧(da da jiang you),终于知道了原因:attr()在二次选中勾选框时,失效. 比如,如下HTML页面,一点[选中].二点[取消选中].三点[选中],瞧,不行了呗. 1.html <!doctype html> <html lang="en"> <head>

  • RecyclerView中监听EditText变化的BUG的解决方法

    需求:有一个列表,列表中有一个edittext(只能输整形),外部有一个整形变量Int,每次改变列表中其中一项的edittext的值时,外部的Int都会改变. 既然这样,我们就需要对edittext进行addTextChangedListener监听,一般做法是在afterTextChanged中对外部进行循环累加,但是想想,每一次你改变edittext都要进行一次时间复杂度为n的循环的话,想想就觉得这个算法很那啥,所以我想了另一个算法,每次改变其中一个item的值时,用总的值减去原item的e

  • vue使用vant中的checkbox实现全选功能

    本文实例为大家分享了vue使用vant中的checkbox实现全选功能的具体代码,供大家参考,具体内容如下 <template> <div class="visiblePeople"> <topbar /> <ul class="list clear_float"> <li v-for="(item, index) in people" :key="index"> &

  • C#提示:“在证书存储区中找不到清单签名证书”的解决方法

    本文实例讲述了C#提示:"在证书存储区中找不到清单签名证书"的解决方法.分享给大家供大家参考.具体分析如下: 一.问题: 程序重新生成,提示错误:在证书存储区中找不到清单签名证书. 二.解决方法: 可能是之前部署的程序证书被我删掉了或是证书过期了,结果出现这个问题.解决方案如下: 方案1:右击项目属性->签名->为ClickOnce清单签名,将勾掉的选项去掉. 方案2:在签名中创建一个新的签名. 方案3:记事本打开相应的csproj文件,调整节点值.<SignMani

  • Android Listview 滑动过程中提示图片重复错乱的原因及解决方法

    主要分析Android中Listview滚动过程造成的图片显示重复.错乱.闪烁的原因及解决方法,顺便跟进Listview的缓存机制. 1.原因分析 Listview item 缓存机制:为了使得性能更优,Listview会缓存行item(某行对应的view).listview通过adapter的getview函数获得每行的item.滑动过程中, a.如果某行item已经划出屏幕,若该item不在缓存内,则put进缓存,否则更新缓存: b.获取滑入屏幕的行item之前会先判断缓存中是否有可用的it

  • Cygwin下安装vim后,vim中退格键无法正常使用的解决方法

    问题描述: 在Cygwin中安装完vim后 进入vim,发现上下左右键和退格键都无法正常使用 问题分析: 首先考虑到的就是缺少vim的配置文件,首先查看/etc路径下是否有vim的配置文件 admin@ThinkPad /etc $ cd /etc admin@ThinkPad /etc $ ls -a|grep vimrc发现/etc下没有vim的全局配置文件,然后再查找当前用户的vim配置文件.vimrc admin@ThinkPad /etc $ cd ~ admin@ThinkPad ~

  • MySQL中Union子句不支持order by的解决方法

    本文实例讲述了MySQL中Union子句不支持order by的解决方法.分享给大家供大家参考,具体如下: 我对DB知之甚少,这问题只在MySQL遇到,不知道别的DBMS是不是也如此. 问题是这样的,我打算在一个表里获得与某一行记录相邻的两行,并且想通过union一起取出来,所以这么写: select id,title from subjects where id>#some_id# order by id limit 1 union select id,title from subjects

  • JS在Chrome浏览器中showModalDialog函数返回值为undefined的解决方法

    本文实例讲述了JS在Chrome浏览器中showModalDialog函数返回值为undefined的解决方法.分享给大家供大家参考,具体如下: 主页面: <script type="text/javascript"> function SelectGroupCust() { var temp = window.showModalDialog("Default2.aspx?xx=" + Date(), "", "dialog

  • 使用nodejs中httpProxy代理时候出现404异常的解决方法

    在公司中使用nodejs构建代理服务器实现前后台分离,代码不能拿出来,然后出现httpProxy代理资源的时候老是出现404.明明被代理的接口是存在的.代码大概如下: var http = require('http'), httpProxy = require('http-proxy'); var proxy = httpProxy.createProxyServer({}); var server = http.createServer(function(req, res) { proxy.

随机推荐