Android实现ListView嵌套Checkbox真正的多选、全选、反选

时间:2021-09-06 09:28:26

Android实现ListView嵌套Checkbox真正的多选、全选、反选

我们在开发APP的时候,很多情况下会使用到ListView嵌套CheckBox的情况,其实很多人要说这个其实很简单了,并没有那么复杂,为什么还要单独用一篇博客来写呢?事实上并非如此,我们在使用ListView嵌套CheckBox复选框的时候会出现很多问题,接下来我将用一篇博客来说明这些问题,给大家一些参照,需要代码的朋友可以到去下载:Demo

废话不多说,直接进入主题,首先我们来看ListView的item的布局文件:

listview_item.xml

<?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="horizontal"
android:gravity="center">

<CheckBox
android:id="@+id/cb_button"
android:checked="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:button="@drawable/checkbox_selector"
android:clickable="true"
android:focusable="true"
android:marginLeft="30dp" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:layout_marginLeft="200dp"
android:text="你好"
android:id="@+id/tv_name"/>

</LinearLayout>

布局文件中使用了一个checkbox复选框和一个textview,这样做是为了解决初学者的疑惑,这里要贴别注意checkbox的xml代码,如果不把android:focusable设置为false的话,会在后面让listview的item无法获得焦点

上面是listview的每个item的布局文件,没什么难度,接下来看看主布局文件:

activity_main.xml

<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_marginTop="100dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<CheckBox
android:id="@+id/cb_all_button"
android:checked="false"
android:button="@drawable/checkbox_selector"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="20dp"
android:text="全选"
android:textSize="15sp"
android:gravity="center_vertical"
android:paddingLeft="10dp"
android:focusable="false"
android:onClick="allSelect" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="20dp"
android:orientation="horizontal" >

<Button
android:id="@+id/btn_fanxuan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:gravity="center"
android:background="@drawable/button_selector"
android:textSize="15sp"
android:text="反选" />

<Button
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:gravity="center"
android:textSize="15sp"
android:background="@drawable/button_selector"
android:text="取消" />

</LinearLayout>
</LinearLayout>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent">

</ListView>
</LinearLayout>

上面的是主界面的布局文件,主文件中放了一个全选的checkbox复选框,和两个按钮用来实现反选取消,也挺简单的,这里就不多说了,其中的background引用的是资源文件,也就是点击的按钮的效果文件,接下来我们看看主要的代码文件:

Adapter适配器

public class MyListAdapter extends BaseAdapter{

List<Test> list = new ArrayList<Test>();
private static SparseBooleanArray isSelected;/**用SparseBooleanArray来代替map**/
Context context;
HolderView holderView = null;
/**
* 全选回调接口
*/

CheckedAllListener mListener;

public void setCheckedAllListener(CheckedAllListener listener) {
mListener = listener;
}


public MyListAdapter(List<Test> list, Context context) {
// TODO Auto-generated constructor stub
this.context = context;
this.list = list;
isSelected = new SparseBooleanArray();
initData();
}

/**
* 初始化数据
*/

private void initData()
{
for (int i = 0; i < list.size(); i++) {

getIsSelected().put(i, false);

}
}


public static SparseBooleanArray getIsSelected()
{
return isSelected;
}

public static void setIsSelected(SparseBooleanArray isSelected) {
MyListAdapter.isSelected = isSelected;
}

@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}

@Override
public Test getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View view = convertView;
if (view == null) {
holderView = new HolderView();
//得到资源文件
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.listview_item, parent, false);
holderView.cb_button = (CheckBox)view.findViewById(R.id.cb_all_button);
holderView.tv_name = (TextView)view.findViewById(R.id.tv_name);
view.setTag(holderView);

}
else {
holderView = (HolderView)view.getTag();
}

final Test item = getItem(position);
if (item != null) {
holderView.tv_name.setText(item.getName());
holderView.cb_button.setChecked(isSelected.get(position));

}
/**
* 增加checkbox的改变事件,每个item的点击事件
*/

holderView.cb_button.setOnCheckedChangeListener(new OnCheckedChangeListener() {

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// TODO Auto-generated method stub
// holderView.cb_button.toggle();
if (buttonView.isPressed()) {

isSelected.put(position,isChecked);
//监听回调,是否改变全选按钮的状态
mListener.CheckAll(isSelected);

}
item.setCheck(isChecked);
}
});
return view;
}


class HolderView
{
private CheckBox cb_button;
private TextView tv_name;
}

/**
* 当所有CheckBox全选时回调
* @author Administrator
*
*/

public interface CheckedAllListener
{


void CheckAll(SparseBooleanArray checkall);

}

}

适配器的内容也比较简单,这里我在适配器中解决了滑动listview出现的checkbox状态错乱的问题,然后增加了一个checkbox的全选回调,就是说每次点击item的复选框都会去调用一下这个借口,来判断对否需要对全选按钮做处理,如果有不懂接口的回调方法,我会在以后的文章中进行讲解,有些不懂的我也用注释标了一下,其中的Test.java比较简单,说白了,就是纯粹的为了好看,帮助大家理解,大家也可以看下:

Test.java

public class Test {
private String name;
private boolean isCheck;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

public boolean isCheck() {
return isCheck;
}
public void setCheck(boolean isCheck) {
this.isCheck = isCheck;
}
}

这个也没有啥好介绍的,然后我们再来看看mainActivity.java文件,

mainActivity.java

public class MainActivity extends AppCompatActivity implements MyListAdapter.CheckedAllListener {

MyListAdapter adapter;
ListView listView;
List<Test> list;
CheckBox cb_button_all;

Button btn_select;
Button btn_select_cancel;

SparseBooleanArray isCheckeds;
//判断是否全选按钮按下
boolean flag = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
listView = (ListView)findViewById(R.id.list_view);
cb_button_all = (CheckBox)findViewById(R.id.cb_all_button);
btn_select = (Button)findViewById(R.id.btn_fanxuan);
btn_select_cancel = (Button)findViewById(R.id.btn_cancel);
setSupportActionBar(toolbar);

isCheckeds = new SparseBooleanArray();
list = new ArrayList<>();
for (int i = 0; i <= 20; i++)
{
Test test = new Test();
test.setName("sister" + i);
list.add(test);
}
adapter = new MyListAdapter(list,this);
adapter.setCheckedAllListener(this);
listView.setAdapter(adapter);

btn_select.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 遍历list的长度,将已选的设为未选,未选的设为已选
for (int i = 0; i < list.size(); i++) {
if (MyListAdapter.getIsSelected().get(i)) {
MyListAdapter.getIsSelected().put(i, false);
} else {
MyListAdapter.getIsSelected().put(i, true);
}
}
// 刷新listview和TextView的显示
adapter.notifyDataSetChanged();
}
});

btn_select_cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

// 遍历list的长度,将已选的按钮设为未选
for (int i = 0; i < list.size(); i++) {
if (MyListAdapter.getIsSelected().get(i)) {
MyListAdapter.getIsSelected().put(i, false);
}
}
// 刷新listview和TextView的显示
adapter.notifyDataSetChanged();
}
});
}

/**
* 该点击事件放在了xml文件的android:onclick中
* @param v
*/

public void allSelect(View v)
{
System.out.println("========>>>>>>" + cb_button_all.isChecked());
if (cb_button_all.isChecked()) {
flag = true;
}
else {
flag = false;
}
if (flag) {
for (int i = 0; i < list.size(); i++) {

isCheckeds.put(i, true);
MyListAdapter.setIsSelected(isCheckeds);
}
}else {
for (int i = 0; i < list.size(); i++) {

isCheckeds.put(i, false);
MyListAdapter.setIsSelected(isCheckeds);
}
}
//更新适配器
adapter.notifyDataSetChanged();
}

/**
* 全选按钮的回调事件,手否进行全选
* @param checkall
*/

@Override
public void CheckAll(SparseBooleanArray checkall) {
int a = checkall.indexOfValue(false);
int b = checkall.indexOfValue(true);
System.out.println(a + "----" + b);
//判断SparseBooleanArray是否含有true
if (checkall.indexOfValue(true) < 0) {

if (cb_button_all.isChecked()) {
this.flag = false;
cb_button_all.setChecked(false);
}

}else if(checkall.indexOfValue(false) < 0){
if (!cb_button_all.isChecked()) {
this.flag = false;
cb_button_all.setChecked(true);
}

}
else if(checkall.indexOfValue(false) >= 0 && checkall.indexOfValue(true) >= 0){
if (cb_button_all.isChecked()) {
this.flag = true;
cb_button_all.setChecked(false);
}
}
}
}

在mainActivity.java中我实现了checkbox全选的回调,然后设置了一个flag来做一个标识,初始化的时候将所有的checkbox设置为false

效果图

Android实现ListView嵌套Checkbox真正的多选、全选、反选

注意事项

在使用ListView嵌套Checkbox的时候一定要注意将CheckBox的焦点设置为false,否则listview的item将无法获得焦点。
再者就是,一定要记得在adapter中处理重复选择,有时候我们如果不进行处理会出现选择错乱的情况,如果道家有不懂得可以和我一起讨论,项目下载地址:项目下载