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

我们在开发APP的时候,很多情况下会使用到ListView嵌套CheckBox的情况,其实很多人要说这个其实很简单了,并没有那么复杂,为什么还要单独用一篇博客来写呢?事实上并非如此,我们在使用ListView嵌套CheckBox复选框的时候会出现很多问题,

废话不多说,直接进入主题,首先我们来看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
效果图
在这里插入图片描述

读者福利

加群免费领取安卓进阶学习视频,源码,面试资料,群内有大牛一起交流讨论技术;【1007478004】。 (包括跨平台开发(Flutter,Weex)、java基础与原理,自定义控件、NDK、架构设计、性能优化、完整商业项目开发等)
点击加群链接:Android高级架构群

阿里P7系列视频教程.png

猜你喜欢

转载自blog.csdn.net/weixin_44941011/article/details/89886950