• 【转】带checkbox的ListView实现(二)——自定义Checkable控件的实现方法


    原文网址:http://blog.csdn.net/harvic880925/article/details/40475367

    前言:前一篇文章给大家展示了传统的Listview的写法,但有的时候我们并不想在DataHolder类中加一个标识是否选中的checked的成员变量,因为在项目开发中,大部分的ListItemLayout布局都是大家共用的,有些人根本不需要checkbox控件,所以会在初始化的时候把这个控件给隐藏掉,但我们的DataHolder在构造的时候以及ListItemAdapter在渲染的时候都需要checked变量,这就有点坑了,所以要想办法能让checkbox不与数据相关联,可能我说到这大家都不大明白我在说什么,其实我也感觉我没讲明白,没关系,往下看,等这篇文章看完之后,如果还没明白,那就给我留言吧。

    相关文章:

    1、《带checkbox的ListView实现(一)——数据与渲染完全分离的传统实现方式》
    2、《带checkbox的ListView实现(二)——自定义Checkable控件的实现方法》
    3、《带checkbox的ListView实现(三)——CheckableImageView的实现方法》

    同样,还是先给大家看看效果图:(与上篇的效果一样,其实我就是用的上一篇的效果图偷笑

    同样,我们还是从布局开始讲。

    一、activity_main.xml ——MainActivity布局文件

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="match_parent"  
    5.     tools:context="com.harvic.trylistviewcheckbox.MainActivity" >  
    6.   
    7.     <ListView  
    8.         android:id="@+id/list"  
    9.         android:layout_width="match_parent"  
    10.         android:layout_height="wrap_content"  
    11.         android:layout_marginBottom="100dp"  
    12.         android:choiceMode="multipleChoice"  
    13.         android:dividerHeight="1px"  
    14.         android:scrollbars="none" />  
    15.       
    16.     <Button android:id="@+id/all_sel"  
    17.         android:layout_width="fill_parent"  
    18.         android:layout_height="wrap_content"  
    19.         android:layout_marginBottom="50dip"  
    20.         android:layout_gravity="bottom"  
    21.         android:text="全选"  
    22.         />  
    23.     <Button android:id="@+id/all_unsel"  
    24.         android:layout_width="fill_parent"  
    25.         android:layout_height="wrap_content"  
    26.         android:layout_gravity="bottom"  
    27.         android:text="全部取消"/>  
    28. </FrameLayout>  

    注意啦:

    这里的布局是与上一篇一样一样的,一个ListView两个按钮。但这里有个参数必须要添加:

    在ListView中:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. android:choiceMode="multipleChoice"  

    这个很重要,android:choiceMode中有三个取值:none、singleChoice、multipleChoice;分别代表:不可选择,单选和多选。在这里,不同的取值对ListView的影响是不一样的,但这个影响仅对ListView的Item自己带有checkable属性时才起作用,对于没有checkable属性的ListView是没有任何效果的,比如,你把android:choiceMode这个参数放在上篇的代码中,无论你取什么值都没有任何作用,具体为什么,下面再讲。

    二、check_list_item.xml —— 单个Item布局

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <?xml version="1.0" encoding="utf-8"?>  
    2.   
    3. <com.harvic.trylistviewcheckbox.CheckableFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    4.     android:layout_width="match_parent"  
    5.     android:layout_height="68dp">  
    6.   
    7.     <CheckBox  
    8.         android:layout_width="wrap_content"  
    9.         android:layout_height="wrap_content"  
    10.         android:layout_gravity="right|center_vertical"  
    11.         android:button="@drawable/checkbox_selector"  
    12.         android:clickable="false"  
    13.         android:focusable="false" />  
    14.   
    15.     <LinearLayout  
    16.         android:layout_width="match_parent"  
    17.         android:layout_height="match_parent"  
    18.         android:layout_marginBottom="17dp"  
    19.         android:layout_marginTop="17dp"  
    20.         android:orientation="vertical">  
    21.   
    22.         <TextView  
    23.             android:id="@+id/title"  
    24.             android:layout_width="wrap_content"  
    25.             android:layout_height="wrap_content"  
    26.             android:textSize="16sp" />  
    27.   
    28.         <TextView  
    29.             android:id="@+id/subtitle"  
    30.             android:textSize="12sp"  
    31.             android:layout_width="wrap_content"  
    32.             android:layout_height="wrap_content" />  
    33.     </LinearLayout>  
    34.   
    35. </com.harvic.trylistviewcheckbox.CheckableFrameLayout>  

    这里与上一篇有两个地方不同:

    1、很明显,使用的自定义控件CheckableFrameLayout;

    2、CheckBox控件没有定义ID值,如果根据上一篇的方式,大家可能会打个问号,没有ID怎么获取这个控件,又怎么让它显示选中与不选中呢。
    下面看看自定义的控件类CheckableFrameLayout的实现:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.harvic.trylistviewcheckbox;  
    2.   
    3. import android.content.Context;  
    4. import android.util.AttributeSet;  
    5. import android.view.View;  
    6. import android.widget.Checkable;  
    7. import android.widget.FrameLayout;  
    8.   
    9. public class CheckableFrameLayout extends FrameLayout implements Checkable {  
    10.   
    11.     public CheckableFrameLayout(Context context, AttributeSet attrs) {  
    12.         super(context, attrs);  
    13.     }  
    14.   
    15.     private boolean mChecked = false;  
    16.   
    17.     @Override  
    18.     public void toggle() {  
    19.         setChecked(!mChecked);  
    20.     }  
    21.   
    22.     @Override  
    23.     public boolean isChecked() {  
    24.         return mChecked;  
    25.     }  
    26.   
    27.     @Override  
    28.     public void setChecked(boolean checked) {  
    29.         if (mChecked != checked) {  
    30.             mChecked = checked;  
    31.             refreshDrawableState();  
    32.             for (int i = 0, len = getChildCount(); i < len; i++) {  
    33.                 View child = getChildAt(i);  
    34.                 if(child instanceof Checkable){  
    35.                     ((Checkable) child).setChecked(checked);  
    36.                 }  
    37.             }  
    38.         }  
    39.     }  
    40.   
    41. }  

    因为我们用到的是FrameLayout控件,所以这里派生自FrameLayout,如果用到诸如LinearLayout,RelativeLayout控件等,就要改成对应的类。最后还继承了Checkable接口;

    FrameLayout没什么好讲的,就是帧布局。关键问题在于Checkable接口,凡实现这个接口的控件都将具有可选中状态的属性;在普通控件中,具有这个状态的有:CheckBox, CheckedTextView, CompoundButton, RadioButton, ToggleButton等。(《Android 中文API (33) —— Checkable》)对于如何展示它的选中状态需要用户自主实现三个函数:isChecked(),toggle(),setChecked();也就是说,当用户使用ListView.setItemChecked(int position,bool checked)函数来将指定的Item设为Checked标识时,就会自动调用这里的SetChecked()函数。在代码中,我们如何设计,用户界面就会显示怎样的选中状态;

    把SetChecked()函数单独拿出来看看:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public void setChecked(boolean checked) {  
    2.     if (mChecked != checked) {  
    3.         mChecked = checked;  
    4.         refreshDrawableState();  
    5.         for (int i = 0, len = getChildCount(); i < len; i++) {  
    6.             View child = getChildAt(i);  
    7.             if(child instanceof Checkable){  
    8.                 ((Checkable) child).setChecked(checked);  
    9.             }  
    10.         }  
    11.     }  
    12. }  

    由于CheckableFrameLayout在ListItem的布局中有三个控件,两个TextView和一个checkbox;所以当用户设置点击属性时,我们要将CheckableFrameLayout的子控件checkbox根据传进来的checked参数更改当前的状态,所以对它下面的所有子控件进行轮循,因为checkbox是具有checkable属性的,所以如果此控件具有checkable属性说明肯定会checkbox,就将它设置为用户指定的checked状态;

    三、ViewHolder——ListItem对应的视图类

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public class ViewHolder{  
    2.     public TextView mTitle;  
    3.     public TextView mSubTitile;   
    4. };  

    注意,这里没有了checkbox控件所对应的mCheckBox对象;

    四、DataHolder——ListItem对应的数据类

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.harvic.trylistviewcheckboxdata;  
    2.   
    3. public class DataHolder{  
    4.     public String titleStr;  
    5.     public String subTitleStr;  
    6.       
    7.     public DataHolder(String title,String subTitle){  
    8.         titleStr = title;  
    9.         subTitleStr = subTitle;       
    10.     }  
    11. }  

    同样,这里有标识主标题文字的titleStr和标识副标题的subTitleStr;但没有了标识选中状态的checked变量,因为我们的控件选中的处理是listviewItem自己完成的。

    五、ListItemAdapter

    同样,先列出全部代码,然后再看看有什么不同。
    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.harvic.trylistviewcheckbox;  
    2.   
    3. import java.util.List;  
    4.   
    5. import com.example.trylistviewcheckbox.R;  
    6.   
    7. import android.content.Context;  
    8. import android.view.LayoutInflater;  
    9. import android.view.View;  
    10. import android.view.ViewGroup;  
    11. import android.widget.BaseAdapter;  
    12. import android.widget.TextView;  
    13.   
    14. public class ListitemAdapter extends BaseAdapter {  
    15.   
    16.     private List<DataHolder> mList;  
    17.     private Context mContext;  
    18.     private LayoutInflater mInflater;  
    19.     public ListitemAdapter(Context context,List<DataHolder> list){  
    20.         mList = list;  
    21.         mContext = context;  
    22.         mInflater = LayoutInflater.from(context);  
    23.     }  
    24.     @Override  
    25.     public int getCount() {  
    26.         // TODO Auto-generated method stub  
    27.         return mList.size();  
    28.     }  
    29.   
    30.     @Override  
    31.     public Object getItem(int position) {  
    32.         // TODO Auto-generated method stub  
    33.         return mList.get(position);  
    34.     }  
    35.   
    36.     @Override  
    37.     public long getItemId(int position) {  
    38.         // TODO Auto-generated method stub  
    39.         return position;  
    40.     }  
    41.   
    42.     @Override  
    43.     public View getView(int position, View convertView, ViewGroup parent) {  
    44.         // TODO Auto-generated method stub  
    45.         ViewHolder holder = null;    
    46.         if (convertView == null) {    
    47.                 
    48.             holder=new ViewHolder();      
    49.                 
    50.             convertView = mInflater.inflate(R.layout.check_list_item, null);     
    51.             holder.mTitle = (TextView)convertView.findViewById(R.id.title);    
    52.             holder.mSubTitile = (TextView)convertView.findViewById(R.id.subtitle);    
    53.             convertView.setTag(holder);    
    54.                 
    55.         }else {    
    56.                 
    57.             holder = (ViewHolder)convertView.getTag();    
    58.         }    
    59.              
    60.         holder.mTitle.setText((String)mList.get(position).titleStr);    
    61.         holder.mSubTitile.setText((String)mList.get(position).subTitleStr);    
    62.         return convertView;    
    63.     }  
    64.       
    65. }  

    在渲染等等上基本一样,唯一不同之处在于没有了对checkbox控件的处理。

    六、 MainActivity

    先看看MainActivity的全部代码,然后慢慢讲。

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.harvic.trylistviewcheckbox;  
    2.   
    3.   
    4. import java.util.ArrayList;  
    5. import java.util.List;  
    6.   
    7. import com.example.trylistviewcheckbox.R;  
    8.   
    9. import android.app.Activity;  
    10. import android.os.Bundle;  
    11. import android.view.Menu;  
    12. import android.view.MenuItem;  
    13. import android.view.View;  
    14. import android.widget.Button;  
    15. import android.widget.ListView;  
    16.   
    17. public class MainActivity extends Activity {  
    18.   
    19.     @Override  
    20.     protected void onCreate(Bundle savedInstanceState) {  
    21.         super.onCreate(savedInstanceState);  
    22.         setContentView(R.layout.activity_main);  
    23.           
    24.         //构造数据  
    25.         final List<DataHolder> dataList = new ArrayList<DataHolder>();  
    26.         for(int i=0;i<10;i++){  
    27.             dataList.add(new DataHolder("harvic的blog------"+i,"harvic"));  
    28.         }  
    29.         //构造Adapter  
    30.         ListitemAdapter adapter = new ListitemAdapter(MainActivity.this, dataList);  
    31.         final ListView listView = (ListView)findViewById(R.id.list);  
    32.         listView.setAdapter(adapter);  
    33.           
    34.         //全部选中按钮的处理  
    35.         Button all_sel = (Button)findViewById(R.id.all_sel);  
    36.         Button all_unsel = (Button)findViewById(R.id.all_unsel);  
    37.         all_sel.setOnClickListener(new View.OnClickListener() {  
    38.               
    39.             @Override  
    40.             public void onClick(View v) {  
    41.                 // TODO Auto-generated method stub  
    42.                 for(int i=0;i<dataList.size();i++){  
    43.                     listView.setItemChecked(i, true);  
    44.                 }  
    45.             }  
    46.         });  
    47.           
    48.         //全部取消按钮处理  
    49.         all_unsel.setOnClickListener(new View.OnClickListener() {  
    50.               
    51.             @Override  
    52.             public void onClick(View v) {  
    53.                 // TODO Auto-generated method stub  
    54.                 for(int i=0;i<dataList.size();i++){  
    55.                     listView.setItemChecked(i, false);  
    56.                 }  
    57.             }  
    58.         });  
    59.     }  
    60.   
    61. }  

    先看一下处理步骤:构造数据-》构造Adapter并设置

    但!但!但!没有listView.setOnItemClickListener()的设置!!!!!!!!!!!

    到这里大家可能要疑问了,为什么没有设计ItemClick的设置,它依然能处理checkbox的事件响应呢?当然是因为我们前面让自定义的CheckableFrameLayout具有了标识可以选中状态的Checkable接口,所以ListView的每一个Item现在都具有Checkbox的点击属性,换句话说,我们的CheckableFrameLayout其实就是扩展的Checkbox控件!当然也可以对ListView设置OnItemClick事件监听,用以处理Item项点击事件;

    再看看全选按钮的实现:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. all_sel.setOnClickListener(new View.OnClickListener() {  
    2.       
    3.     @Override  
    4.     public void onClick(View v) {  
    5.         // TODO Auto-generated method stub  
    6.         for(int i=0;i<dataList.size();i++){  
    7.             listView.setItemChecked(i, true);  
    8.         }  
    9.     }  
    10. });  

    因为我们的每个ListViewItem都具有Checkable属性,所以调用ListView的setItemChecked()函数是有作用的,如果我们的ListviewItem不具有Checkable属性,那么无论你怎么调用SetItemChecked函数都不会起什么效果!

    全部取消也很简单了,同样是轮循所有的Item,然后把利用SetItemChecked(i,false)即可。

    七、附:获取所有选中行的行号

    上面,我们基本实现了自定义控件实现自动选中checkbox的功能,但有时,我们在选中后,更需要知道,我们都选中了哪些,下面的代码就实现了这个功能:其中position就是获取到了当前选中行的position

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. SparseBooleanArray checkedArray = mListView.getCheckedItemPositions();  
    2. for (int i = 0; i < checkedArray.size(); i++) {  
    3.     if (checkedArray.valueAt(i)){  
    4.         ……//i就是选中的行号  
    5.     }  
    6. }  

    OK啦 ,到这就全部讲完了,对于自定义控件的这部分涉及的到东东比较多,不知道大家理解的怎么样,如果有哪部分没理解的话,在评论中回复下,我再修改内容。或许能直接给出应该怎么组织本文的建议就更好不过了,谢谢。

    如果本文有帮到你,记得加关注哦!

    源码下载地址:http://download.csdn.net/detail/harvic880925/8083133

    请大家尊重原创者版权,转载请标明出处哦:http://blog.csdn.net/harvic880925/article/details/40475367  谢谢!

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    深入理解vue路由的使用
    mac异常删除管理员账户恢复操作
    springMVC前后端分离开发模式下支持跨域请求
    npm 更新镜像安装Appium
    npm升级所有可更新包
    new AppiumDriver<>(new URL(url), capabilities) 报错 java.lang.NoSuchMethodError: com.google.common.base.Throwables.throwIfUnchecked(Ljava/lang/Throwable;)V
    Jmeter命令行运行实例讲解
    shodan会员命令版
    AS-REPRoasting
    Password Spraying/密码喷洒
  • 原文地址:https://www.cnblogs.com/wi100sh/p/4967728.html
Copyright © 2020-2023  润新知