项目回顾-RecyclerView和CheckBox错乱问题
如果在item布局中引入CheckBox,而不作任何处理的话,在RecyclerView滑动的时候,选中状态都会出现错乱。
解决思路是:用map保存每一个被勾选的对象的特定信息,比如id,在onBindViewHolder中获取当前对象,判断map中是否包含当前对象的id,是则setChecked为true,否则为false
效果
废话不说,上代码
首先实体Box 一个int id 一个String name
public class Box { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
item_cb.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rl_cb" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="15dp" > <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher"/> <TextView android:id="@+id/tv_cb" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_toRightOf="@+id/imageView" android:paddingLeft="10dp" android:text="cb" /> <CheckBox android:id="@+id/cb" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_centerVertical="true"/> </RelativeLayout>
activity
首先两个重要参数 isMulChoice 判断当前是否为多选状态;selectid为保存状态的map
initView()操作 初始化recyclerView 和adapter 的点击事件
setData() 手动添加了30个数据
长按 item时 isMulChoice 为真,此时notifyDataSetChanged会调用adapter的 onBindViewHolder,由于isMulChoice 为真了,隐藏的CheckBox会显示出来,同时主页面隐藏的删除、撤销按钮的布局也会出现
点击撤销按钮 map会清空,isMulChoice 为假,notifyDataSetChanged隐藏CheckBox,同时按钮的布局隐藏
点击删除按钮 会遍历map,找到每一个选中对象的id,之后交给adapter操作,最后执行一下撤销按钮的操作
1 public class CbActivity extends AppCompatActivity { 2 3 4 public static boolean isMulChoice=false; 5 public static Map<Integer, String> selectid = new HashMap<>(); 6 7 private LinearLayoutManager linearLayoutManager; 8 public List<Box> list; 9 private CbAdapter adapter; 10 11 @Bind(R.id.rcv) 12 RecyclerView recyclerView; 13 14 @Bind(R.id.ll_btn) 15 LinearLayout ll_btn; 16 17 @OnClick(R.id.btn_cancle) 18 void btn_cancle(){ 19 selectid.clear(); 20 ll_btn.setVisibility(View.GONE); 21 isMulChoice = false; 22 adapter.notifyDataSetChanged(); 23 } 24 25 @OnClick(R.id.btn_del) 26 void btn_del(){ 27 if (selectid.size()==0){ 28 Toast.makeText(this,"至少选中一条数据",Toast.LENGTH_SHORT).show(); 29 }else { 30 31 Set<Integer> mapSet = selectid.keySet(); 32 Iterator<Integer> itor=mapSet.iterator(); 33 while (itor.hasNext()){ 34 int id = itor.next(); 35 if (selectid.get(id).equals("true")){ 36 Logger.e("要删除的id :"+id); 37 adapter.remove(id); 38 } 39 } 40 btn_cancle(); 41 } 42 } 43 44 45 @Override 46 protected void onCreate(Bundle savedInstanceState) { 47 super.onCreate(savedInstanceState); 48 setContentView(R.layout.activity_cb); 49 ButterKnife.bind(this); 50 51 initView(); 52 setData(); 53 } 54 55 private void setData() { 56 57 List<Box> boxes=new ArrayList<>(); 58 for (int i=0;i<30;i++){ 59 Box box=new Box(); 60 box.setId(i); 61 box.setName("name"+i); 62 boxes.add(box); 63 } 64 65 adapter.addAll(boxes); 66 67 } 68 69 private void initView() { 70 adapter = new CbAdapter(); 71 linearLayoutManager=new LinearLayoutManager(this); 72 recyclerView.setLayoutManager(linearLayoutManager); 73 recyclerView.setAdapter(adapter); 74 recyclerView.setItemAnimator(new DefaultItemAnimator()); 75 76 adapter.setOnClickListener(new CbAdapter.OnClick() { 77 @Override 78 public void OnItemClick(View view, int position) { 79 // switch (view.getId()){ 80 // case R.id.cb: 81 // Logger.e("cb "+position); 82 // break; 83 // } 84 } 85 86 @Override 87 public void OnItemLongClick(View view, int position) { 88 switch (view.getId()){ 89 case R.id.rl_cb: 90 isMulChoice=true; 91 ll_btn.setVisibility(View.VISIBLE); 92 adapter.notifyDataSetChanged(); 93 Logger.e("rl_cb OnItemLongClick"+position); 94 break; 95 } 96 } 97 98 @Override 99 public void OnItemChecked(View view, int position, boolean isChecked) { 100 101 } 102 }); 103 } 104 }
adapter
先看 onBindViewHolder
第49行 cbHolder.cb.setVisibility(CbActivity.isMulChoice?View.VISIBLE:View.INVISIBLE); CheckBox的状态是由activity里的isMulChoice控制
设置 setOnCheckedChangeListener事件,判断是否选中
如果选中 true,再判断selectid中是否包含当前点击的对象的id,如果没有就添加进去
如果取消选中 false,再判断selectid中是否包含当前点击的对象的id,如果有就删除
而CheckBox的选中与否的状态就是selectid是否包含id 来做判断的
删除操作 根据传入的id来遍历list,remove,最后更新。当然删除可能有更好的方法
1 public class CbAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { 2 3 private List<Box> list=new ArrayList<>(); 4 5 public List<Box> getList(){ 6 return list; 7 } 8 9 public void addAll(List<Box> boxes){ 10 list.addAll(boxes); 11 notifyDataSetChanged(); 12 } 13 14 public void remove(int id){ 15 for (int i=0;i<list.size();i++){ 16 if (id==list.get(i).getId()){ 17 list.remove(list.get(i)); 18 } 19 } 20 notifyDataSetChanged(); 21 } 22 23 public interface OnClick { 24 void OnItemClick(View view,int position); 25 void OnItemLongClick(View view,int position); 26 void OnItemChecked(View view,int position,boolean isChecked); 27 } 28 29 private OnClick onClick; 30 31 public void setOnClickListener(OnClick click){ 32 this.onClick=click; 33 } 34 35 @Override 36 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 37 View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_cb,parent,false); 38 return new CbHolder(view); 39 } 40 41 @Override 42 public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { 43 if (holder instanceof CbHolder){ 44 CbHolder cbHolder= (CbHolder) holder; 45 final Box box=list.get(position); 46 47 cbHolder.tv_cb.setText( "id "+box.getId()+ " "+box.getName()); 48 49 cbHolder.cb.setVisibility(CbActivity.isMulChoice?View.VISIBLE:View.INVISIBLE); 50 51 if (position%2==0){ 52 cbHolder.rl_cb.setBackgroundResource(R.color.gray); 53 }else { 54 cbHolder.rl_cb.setBackgroundResource(R.color.white); 55 56 } 57 58 if (onClick!=null){ 59 60 cbHolder.rl_cb.setOnLongClickListener(new View.OnLongClickListener() { 61 @Override 62 public boolean onLongClick(View view) { 63 onClick.OnItemLongClick(view,position); 64 return true; 65 } 66 }); 67 } 68 69 cbHolder.cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 70 @Override 71 public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) { 72 if (isChecked){ 73 if (!CbActivity.selectid.containsKey(box.getId())){ 74 CbActivity.selectid.put(box.getId(),"true"); 75 Logger.e("put "+box.getId()); 76 } 77 }else { 78 if (CbActivity.selectid.containsKey(box.getId())){ 79 CbActivity.selectid.remove(box.getId()); 80 Logger.e("remove "+box.getId()); 81 } 82 } 83 84 } 85 }); 86 87 if (CbActivity.selectid!=null){ 88 cbHolder.cb.setChecked(CbActivity.selectid.containsKey(box.getId())?true:false); 89 }else { 90 cbHolder.cb.setChecked(false); 91 } 92 } 93 } 94 95 @Override 96 public int getItemCount() { 97 return list==null?0:list.size(); 98 } 99 100 class CbHolder extends RecyclerView.ViewHolder{ 101 102 @Bind(R.id.tv_cb) 103 TextView tv_cb; 104 @Bind(R.id.cb) 105 CheckBox cb; 106 @Bind(R.id.rl_cb) 107 RelativeLayout rl_cb; 108 109 public CbHolder(View itemView) { 110 super(itemView); 111 ButterKnife.bind(this,itemView); 112 } 113 } 114 }
activity的布局
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.demon.testservice.ui.CbActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/rcv" android:layout_width="match_parent" android:layout_height="match_parent"/> <LinearLayout android:id="@+id/ll_btn" android:visibility="gone" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:weightSum="4" android:layout_gravity="center_horizontal|bottom"> <Button android:id="@+id/btn_cancle" android:text="撤销" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content"/> <Button android:id="@+id/btn_xxx" android:visibility="invisible" android:layout_width="0dp" android:layout_weight="2" android:layout_height="wrap_content"/> <Button android:id="@+id/btn_del" android:text="删除" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content"/> </LinearLayout> </FrameLayout>
最后还有一个地方要注意一下
虽然我在RecyclerView尝试自定义了一下CheckBox的setOnCheckedChangeListener事件的接口,不过貌似没有用
另外 CheckBox 不要同时设置 setOnClickListener 和 setOnCheckedChangeListener,它只会响应前者