Android 中的 Checkbox 详解

澳门新葡亰3522平台游戏 2

在Activity中使用

效果:

澳门新葡亰3522平台游戏 1

For more questions , contacts me by :

cquptzx@qq.com or 
cquptzx@outlook.com

 

0和1是计算机的基础,数理逻辑中0和1代表两种状态,真与假.0和1看似简单,其实变化无穷.
今天我就来聊聊android控件中拥有着0和1这种特性的魔力控件checkbox.

原始代码:

package irdc.EX04_05;

import
android.app.Activity;

import
android.os.Bundle;

import
android.widget.CheckBox;

import
android.widget.CompoundButton;

import
android.widget.TextView;

publicclass EX04_05 extends
Activity

{

 
/*声明对象变量*/

 
private
TextView mTextView1;

 
private
CheckBox mCheckBox1;

 
private
CheckBox mCheckBox2;

 
private
CheckBox mCheckBox3;

 
/**
Called when the activity is first created. */

 
@Override

 
publicvoid
onCreate(Bundle savedInstanceState)

 
{

澳门新葡亰3522平台游戏,   
super.onCreate(savedInstanceState);

   
setContentView(R.layout.main);

   
/*透过findViewById取得TextView对象并调整文字内容*/

   
mTextView1 =
(TextView) findViewById(R.id.myTextView1);

   
mTextView1.setText(“你所选择的项目有:
“);

   
/*透过findViewById取得三个CheckBox对象*/

   
mCheckBox1=(CheckBox)findViewById(R.id.myCheckBox1);

   
mCheckBox2=(CheckBox)findViewById(R.id.myCheckBox2);

   
mCheckBox3=(CheckBox)findViewById(R.id.myCheckBox3);

   
/*设定OnCheckedChangeListener给三个CheckBox对象*/

   
mCheckBox1.setOnCheckedChangeListener(mCheckBoxChanged);

   
mCheckBox2.setOnCheckedChangeListener(mCheckBoxChanged);

   
mCheckBox3.setOnCheckedChangeListener(mCheckBoxChanged);

   
}

 
/*声明并建构onCheckedChangeListener对象*/

 
private
CheckBox.OnCheckedChangeListener mCheckBoxChanged =

   
new
CheckBox.OnCheckedChangeListener()

  {

   
/*implement
onCheckedChanged方法*/

   
@Override

   
publicvoid
onCheckedChanged(

       
CompoundButton buttonView, boolean
isChecked) {

     
//
TODO
Auto-generated method stub

     
/*透过getString()取得CheckBox的文字字符串*/

     
String str0=”所选的项目为:
“;

     
String str1=getString(R.string.str_checkbox1);

     
String str2=getString(R.string.str_checkbox2);

     
String str3=getString(R.string.str_checkbox3);

     
String plus=”;”;

     
String result=”但是超过预算啰!!”;

     
String result2=”还可以再多买几本喔!!”;

     
/*任一CheckBox被勾选后,该CheckBox的文字会改变TextView的文字内容 *
三个对象总共八种情境*/

     
if(mCheckBox1.isChecked()==true &
mCheckBox2.isChecked()==true &
mCheckBox3.isChecked()==true)

     
{

       
mTextView1.setText(str0+str1+plus+str2+plus+str3+result);

       
}

     
elseif(mCheckBox1.isChecked()==false &
mCheckBox2.isChecked()==true &
mCheckBox3.isChecked()==true)

       
{

         
mTextView1.setText(str0+str2+plus+str3+result);

         
}

       
elseif(mCheckBox1.isChecked()==true &
mCheckBox2.isChecked()==false &
mCheckBox3.isChecked()==true)

         
{

           
mTextView1.setText(str0+str1+plus+str3+result);

           
}

         
else 
if(mCheckBox1.isChecked()==true &
mCheckBox2.isChecked()==true &
mCheckBox3.isChecked()==false)

           
{

             
mTextView1.setText(str0+str1+plus+str2+result);
}

           
elseif(mCheckBox1.isChecked()==false &
mCheckBox2.isChecked()==false &
mCheckBox3.isChecked()==true)

             
{

               
mTextView1.setText(str0+str3+plus+result2);

               
}

             
elseif(mCheckBox1.isChecked()==false &
mCheckBox2.isChecked()==true &
mCheckBox3.isChecked()==false)

               
{

                 
mTextView1.setText(str0+str2);

                 
}

               
elseif(mCheckBox1.isChecked()==true &
mCheckBox2.isChecked()==false &
mCheckBox3.isChecked()==false)

                 
{ mTextView1.setText(str0+str1);
}

                 
elseif(mCheckBox1.isChecked()==false &
mCheckBox2.isChecked()==false &
mCheckBox3.isChecked()==false)

                 
{ mTextView1.setText(str0);

                 
}

     
}

     
};

     
}

缺点分析:条件语句过于冗长,难看,难修改.

CheckBox cbx = (CheckBox) findViewById(R.id.cbx);
cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        //do something
    }
});

改进代码:

(使用条件表达式替代if else赋值.)

package
edu.cquptzx.MutiCheckBox;

 

import
android.app.Activity;

import
android.os.Bundle;

import
android.widget.CheckBox;

import
android.widget.CompoundButton;

import
android.widget.TextView;

 

publicclass
MutiCheckBoxActivity extends
Activity {

   
private
TextView tv;

   
private
CheckBox cb1,cb2,cb3;

   
private
String str1,str2,str3
,str_BR,str_NULLBILL;

   
privateintflag1 =
0,flag2 =
0,flag3 = 0 ,
total = 0
;

   
/**
Called when the activity is first created. */

   
@Override

   
publicvoid
onCreate(Bundle savedInstanceState) {

       
super.onCreate(savedInstanceState);

       
setContentView(R.layout.main);

       

       
//根据ID找到对象;

       
tv =
(TextView)findViewById(R.id.listBill);

       
cb1 =
(CheckBox) findViewById(R.id.checkBox1);

       
cb2 =
(CheckBox) findViewById(R.id.checkBox2);

       
cb3 =
(CheckBox) findViewById(R.id.checkBox3);

       

       
str1 =
getString(R.string.item_1);

       
str2 =
getString(R.string.item_2);

       
str3 =
getString(R.string.item_3);

       
str_BR =
getString(R.string.str_BR);

       
str_NULLBILL =
getString(R.string.str_NULLBILL);

       

       
tv.setText(str_NULLBILL);

       

       
CheckBox.OnCheckedChangeListener cblistener = new
CheckBox.OnCheckedChangeListener ()

           
{

             
publicvoid
onCheckedChanged(CompoundButton buttonView, boolean
isChecked)

             
{

 

                  
/*对所有情况进行编码

                  
* 编码表

                  
* 权值
100    10     1   /

                  
*      flag1 flag2  falg3 total

                  
*      0      0      0      0

                  
*      0      0      1      1

                  
*      0      1      0      10

                  
*      0      1      1      11

                  
*      1      0      0      100

                  
*      1      0      1      101

                  
*      1      1      0      110

                  
*      1      1      1      111

                  
* */

                 
flag1 =
(cb1.isChecked()==true) ?
100 : 0;

                 
flag2 =
(cb2.isChecked()==true) ? 10
: 0;

                 
flag3 =
(cb3.isChecked()==true) ? 1
: 0;

                 

                  
/*

                   
if(cb1.isChecked()) 

                 
{

                    
flag1 = 100;

                 
}

                 
else

                 
{

                    
flag1 = 0;

                 
}

                 

                  
if(cb2.isChecked())  

                  
{

                   
flag2 = 10;

                  
}

                  
else

                  
{

                   
flag2 = 0;

                  
}

                  

                  
if(cb3.isChecked())

                  
{

                   
flag3 = 1 ;

                  
}

                  
else

                  
{

                   
flag3 = 0;

                  
}

                   
*/

                  
total =
flag1 +
flag2 +
flag3
;

                  

                  
switch(total)

                  
{

                     
case 0
:

                     
tv.setText(str_NULLBILL);

                     
break;

                     
case
1:

                     
tv.setText(str3);

                     
break;

                     
case 10
:

                     
tv.setText(str2);

                     
break;

                     
case 11
:

                     
tv.setText(str2 +
str_BR +
str3);

                     
break;

                     
case
100:

                     
tv.setText(str1);

                     
break;

                     
case
101:

                     
tv.setText(str1 +
str_BR +
str3);

                     
break;

                     
case
110:

                     
tv.setText(str1 +
str_BR +
str2);

                     
break;

                     
case
111:

                     
tv.setText(str1 +
str_BR +
str2 +
str_BR +
str3);

                     
break;

                     
default:

                     
break;

                  
}                 

             
}          

           
};          

        
cb1.setOnCheckedChangeListener(cblistener);

        
cb2.setOnCheckedChangeListener(cblistener);

        
cb3.setOnCheckedChangeListener(cblistener);     

   
}

}

boolean isChecked= false;
CheckBox cbx = (CheckBox) findViewById(R.id.cbx);
cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if(isChecked){
            //do something
        }else{
            //do something else
        }
    }
});
cbx.setChecked(isChecked);

改进思路:

对各种状况进行编码,使用switch语句,使结构清晰易懂,易于维护.

/*对所有情况进行编码

                  
* 编码表

                  
* 权值
100    10     1   /

                  
*      flag1 flag2  falg3 total

                  
*      0      0      0      0

                  
*      0      0      1      1

                  
*      0      1      0      10

                  
*      0      1      1      11

                  
*      1      0      0      100

                  
*      1      0      1      101

                  
*      1      1      0      110

                  
*      1      1      1      111

                  
* */

随后我会把其他筛选的情况开源,但是最精妙的原理就在于这个简单的例子上.各位看完之后不妨亲自动手试试,感受一下.

4.5消费券采购列表

mada
mada,还有一种情况,如果在onCheckedChanged的时候,isChecked跟mCheckStates.get(pos)一致的话,这会导致什么情况呢.

—多选项CheckBox的应用

目录

4.5消费券采购列表—多选项CheckBox的应用…
1

目标:1

方法:1

原始代码:1

缺点分析:3

改进思路:3

改进代码:3

效果:6

 

目标:设计三个CheckBox,选中其中的后用TextView显示出选择结果.

方法:对CheckBox设置setOnCheckedChangeListener监听器.

简单解释一下:其实重点在PopupWindow里面,MainActivity的CheckBox作为参数传递到了
PopupWindow里.首先,用户点击MainActivity的CheckBox,接着会执行isChecked分支,这样PopupWindow就展示给了用户,这样用户操作的环境就到了PopupWindow里面,等用户选择好筛选条件后,PopupWindow就把筛选条件设给outCbx,然后改变outCbx状态,从而触发MainActivity中onCheckedChanged中的否定分支,此时展示的是一个Toast,实际应用中可以是一个网络请求.同时,由于PopupWindow的代码并没有阻塞操作,所以会接着执行下一句
super.dismiss(),这样你在MainActivity就不用担心PopupWindow的关闭问题啦.最后,在MainActivity中还加入了try-catch来以防万一,这种机制真是太神奇啦.这种机制把筛选操作从Activity中分离了出来,以后我们写筛选可以完全独立于Activity啦,真的是一种很软件工程的做法.

澳门新葡亰3522平台游戏 2

ok,今天的详解就到这儿了.视反馈程度更新博文和
源码 哦!

holder.cbx.setTag(item);
holder.cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        Item item =(Item) buttonView.getTag();
        if(isChecked){
            item.setCheckState(true);
            //do something
        }else{
            item.setCheckState(false);
            //do something else
        }
    }
});
cbx.setChecked(item.getCheckState());

这就会让你的//do something做两次,这么做就是没有必要的啦,而且如果你的//do
something是网络请求的话,这样就会导致更大问题.所以,我们有必要对这种情况做过滤.

这种方法基本正确,但是我们要额外的给每个数据项里面添加一个字段来记录状态,这代价就有点大了.一是不必这么做,二是这会导致本地数据结构跟服务端结构不一致.通常,列表中使用CheckBox的话,很明显是把选中的item给记录下来,可以这么理解,选中的状态是列表给的,而item本身应该是无状态的.那么,如果重构我们的代码呢,Android为我们提供了一种完美的数据结构来解决这个问题.你可以用SparseArray,也可以用SparseBooleanArray,我现在习惯使用SparseBooleanArray,ok,请看代码

private class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    SparseBooleanArray mCheckStates=new SparseBooleanArray();
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //...
    }
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        holder.cbx.setTag(position);
        holder.cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                int pos =(int)buttonView.getTag();
                if(isChecked){
                    mCheckStates.put(pos,true);
                    //do something
                }else{
                    mCheckStates.delete(pos);
                    //do something else
                }
            }
        });
        cbx.setChecked(mCheckStates.get(position,false));
    }
    @Override
    public int getItemCount() {
        //...
    }
}

好啦,如果你能将CheckBox跟SparseBooleanArray联用,并且能考虑到加锁和过滤重选的话,那么说明你使用CheckBox的姿势摆正了.但是,我要讲的精彩的地方才刚刚开始.

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    int pos =(int)buttonView.getTag();
    if(isChecked){
        mCheckStates.put(pos,true);
        //do something
    }else{
        mCheckStates.delete(pos);
        //do something else
    }
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if(lockState)return;
    int pos =(int)buttonView.getTag();
    if(mCheckStates.get(pos,false) == isChecked)return;
    if(isChecked){
        mCheckStates.put(pos,true);
        //do something
    }else{
        mCheckStates.delete(pos);
        //do something else
    }
}

单独用CheckBox很easy,接下来,复杂的情况来啦,CheckBox如何跟ListView/RecyclerView(以下简称LV/RV)配合使用.这就不能简单的考虑问题啦,要知道LV/RV中的视图个数跟数据集的里面的数据并不一致,真正的视图个数远小于数据集中数据项的个数.因为屏幕上在列表中的视图是可以复用的.关于这点,初学者请看
The World Of ListView
.由于LV/RV的复用机制,如果我们没有用数据来控制CheckBox状态的话,将会导致CheckBox的显示在列表中错乱.比方说你只对第一个Item中的CheckBox做了选中操作,当列表向上滚动的时候,你会发现,下面的Item中居然也会有被选中的.当然,我刚学Android时候也遇到过这种情况,问题的关键就在于要用数据来控制视图的显示.因此在getView/onBindViewHolder中,我们应该这么写.

boolean lockState=false;
holder.cbx.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if(lockState)return;
        //不然cbx改变状态.
        lockState=true;
        buttonView.setChecked(!isChecked);
        lockState=false;
        //...
    }
});
public class FrameLayoutCheckBox extends FrameLayout {
    CompoundButton cbx;

    public FrameLayoutCheckBox(Context context) {
        super(context);
    }

    public FrameLayoutCheckBox(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FrameLayoutCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private CheckBox findCheckBox(View view) {
        //无递归广度优先遍历寻找CheckBox - -!我只是想重温一下C
        ArrayList<View> views = new ArrayList<>();
        views.add(view);
        while (!views.isEmpty()) {
            View c = views.remove(0);
            if (c instanceof CheckBox) {
                return (CheckBox) c;
            } else if (c instanceof ViewGroup) {
                ViewGroup fa = (ViewGroup) c;
                for (int i = 0; i < fa.getChildCount(); i++) {
                    views.add(fa.getChildAt(i));
                }
            }
        }
        return null;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() > 0) {
            View child = findCheckBox(this);
            if (child instanceof CompoundButton) cbx = (CompoundButton) child;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (cbx != null) {
            Rect bounds = new Rect(getPaddingLeft(), getPaddingTop(), getPaddingLeft() + getMeasuredWidth() + getPaddingRight(), getPaddingTop() + getMeasuredHeight() + getPaddingBottom());
            TouchDelegate delegate = new TouchDelegate(bounds, cbx);
            setTouchDelegate(delegate);
        }
    }
}

但是这么写的话,就会调用buttonView的onCheckedChanged,其实buttonView就是外面的holder.cbx,这就会造成死循环.因此我们如果用cbx本身去改变状态的话,那么一定要加锁.

<?xml version="1.0" encoding="utf-8"?>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/cbx"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:checked="false" />