带checkbox的ListView实现(一)——数据与渲染完全分离的传统实现方式

前言:这几天在公司跟着他们写代码才发现,公司有那么多的牛人,个个都很厉害,在他们的帮助下,自己提升的很快,性格又极好的一帮人,真的很招人喜欢。这篇文章使用传统的实现方法来实现带CheckBox的ListView的实现,下一篇写写一个牛人教我的,如何使用重写布局控件来实现带CheckBox的ListView的。


DropBox老总回到母校在毕业生典礼的一段话送给大家:大学里植入的一个错误理念就是“先准备好”虽然学习仍然占据首要位置,不过最快的学习方式还是去实践,如果你拥有梦想,你有一生的时间去学习和准备,而你所要做的就是马上实施。诚实地说,我从来没有准备好。


相关文章:

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


先给大家看看实现的效果:



在ListView中,我们一般为了在实现Adapter时,是ListItem渲染与其中的数据完全分离的,可能初学者对数据分离不太清楚,后面我会慢慢讲解。先看主布局(activity_main.xml)

一、activity_main.xml

[html]  view plain copy 在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.   
  6.     <ListView  
  7.         android:id="@+id/list"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:layout_marginBottom="100dp"  
  11.         android:dividerHeight="1px"  
  12.         android:scrollbars="none" />  
  13.       
  14.     <Button android:id="@+id/all_sel"  
  15.         android:layout_width="fill_parent"  
  16.         android:layout_height="wrap_content"  
  17.         android:layout_marginBottom="50dip"  
  18.         android:layout_gravity="bottom"  
  19.         android:text="全选"  
  20.         />  
  21.     <Button android:id="@+id/all_unsel"  
  22.         android:layout_width="fill_parent"  
  23.         android:layout_height="wrap_content"  
  24.         android:layout_gravity="bottom"  
  25.         android:text="全部取消"/>  
  26. </FrameLayout>  
布局没有特别之处,一个LIstView,两个button,没什么特别注意的地方,下面看看单个Item的布局

二、ListItem布局(check_list_item.xml)

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2.   
  3. <FrameLayout 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:id="@+id/checkbox"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_gravity="right|center_vertical"  
  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. </FrameLayout>  

这个布局就是这样的,左边是一个垂直布局的LinearLayout,一个主标题,一个副标题;右边是一个CheckBox;

但这里有个地方要注意:CheckBox一定要添加两个属性:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. android:clickable="false"  
  2. android:focusable="false"   
首先将CheckBox设置为不可点击,这样就不会造成ListView无法捕捉当前CheckBox状态的尴尬局面,如果不理解,把这个参数删除,看看代码运行效果就清楚了。另外要把Checbox的获取焦点的属性取消,不然ListView是无法点击的,因为焦点都在CheckBox那了。(坑爹玩意)
上面我们给出了ListItem的布局,下面看看这个Item对应的数据;

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

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class ViewHolder{  
  2.     public TextView mTitle;  
  3.     public TextView mSubTitile;   
  4.     public CheckBox mCheckBox;  
  5. };  
这里的三个控件完全对应布局里的三个控件。

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

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.harvic.trylistviewcheckboxdata;  
  2.   
  3. public class DataHolder{  
  4.     public String titleStr;  
  5.     public String subTitleStr;  
  6.     public boolean checked;  
  7.       
  8.     public DataHolder(String title,String subTitle,boolean check){  
  9.         titleStr = title;  
  10.         subTitleStr = subTitle;  
  11.         checked=check;        
  12.     }  
  13. }  
这里有三个变量,分别对应三个控件所就显示的数据:titleStr对应主标题文字,subTitleStr对应副标题文字,checked对应checkbox是否选中状态。下面就是有点艰难的部分了,ListitemAdapter的生成。

五、ListitemAdapter

先给出完整代码,然后再细讲:看不懂的话,往下看;

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

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public ListitemAdapter(Context context,List<DataHolder> list){  
  2.     mList = list;  
  3.     mContext = context;  
  4.     mInflater = LayoutInflater.from(context);  
  5. }  
传进来一个Context,一个DataHolder的数组;这个数组就是我们的Adapter要显示的所有数据,我们就要根据这个数组的内容,一行行显示数据。
下面我们看显示数据部分:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public View getView(int position, View convertView, ViewGroup parent) {  
  2.     // TODO Auto-generated method stub  
  3.     ViewHolder holder = null;    
  4.     if (convertView == null) {           
  5.         holder=new ViewHolder();      
  6.             
  7.         convertView = mInflater.inflate(R.layout.check_list_item, null);     
  8.         holder.mTitle = (TextView)convertView.findViewById(R.id.title);    
  9.         holder.mSubTitile = (TextView)convertView.findViewById(R.id.subtitle);    
  10.         holder.mCheckBox = (CheckBox)convertView.findViewById(R.id.checkbox);    
  11.         convertView.setTag(holder);            
  12.     }else {    
  13.             
  14.         holder = (ViewHolder)convertView.getTag();    
  15.     }    
  16.          
  17.     holder.mTitle.setText((String)mList.get(position).titleStr);    
  18.     holder.mSubTitile.setText((String)mList.get(position).subTitleStr);    
  19.     holder.mCheckBox.setChecked(mList.get(position).checked);    
  20.     return convertView;    
  21. }  
至于为什么要判断ConvertView是否为空以及setTag()和getTag()的问题,看这篇文章: 《BaseAdapter——convertView回收机制与动态控件响应》
在getview函数中的如果这个ConvertView是新建的,那么就会通过获取R.layout.check_list_item中的控件,并与ViewHolder中的变量绑定;
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. holder=new ViewHolder();      
  2.     
  3. convertView = mInflater.inflate(R.layout.check_list_item, null);     
  4. holder.mTitle = (TextView)convertView.findViewById(R.id.title);    
  5. holder.mSubTitile = (TextView)convertView.findViewById(R.id.subtitle);    
  6. holder.mCheckBox = (CheckBox)convertView.findViewById(R.id.checkbox);    
  7. convertView.setTag(holder);     
至于回收来的ConvertView是已经绑定好了的,所以就不必重新生成ViewHolder,不懂的话看上面的 《BaseAdapter——convertView回收机制与动态控件响应》
然后根据当前这个<DataHolder> list中的数据来显示当前Item的数据;

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. holder.mTitle.setText((String)mList.get(position).titleStr);    
  2. holder.mSubTitile.setText((String)mList.get(position).subTitleStr);    
  3. holder.mCheckBox.setChecked(mList.get(position).checked);    

六、 MainActivity

同样,先给出全部代码,然后逐步讲解:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.harvic.trylistviewcheckboxdata;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import android.app.Activity;  
  7. import android.os.Bundle;  
  8. import android.view.View;  
  9. import android.widget.AdapterView.OnItemClickListener;  
  10. import android.widget.AdapterView;  
  11. import android.widget.Button;  
  12. import android.widget.ListView;  
  13.   
  14. public class MainActivity extends Activity {  
  15.   
  16.     @Override  
  17.     protected void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.activity_main);  
  20.           
  21.         //初始化数据  
  22.         final List<DataHolder> dataList = new ArrayList<DataHolder>();  
  23.         for (int i = 0; i < 10; i++) {  
  24.             dataList.add(new DataHolder("harvic的blog------" + i, "harvic",  
  25.                     false));  
  26.         }  
  27.   
  28.         //构造Adapter  
  29.         final ListitemAdapter adapter = new ListitemAdapter(MainActivity.this,dataList);  
  30.           
  31.         //设置adapter  
  32.         final ListView listView = (ListView) findViewById(R.id.list);  
  33.         listView.setAdapter(adapter);  
  34.         listView.setOnItemClickListener(new OnItemClickListener() {  
  35.   
  36.             @Override  
  37.             public void onItemClick(AdapterView<?> parent, View view,  
  38.                     int position, long id) {  
  39.                 // TODO Auto-generated method stub  
  40.                 boolean checked = dataList.get(position).checked;  
  41.                 if (!checked) {  
  42.                     dataList.get(position).checked = true;  
  43.                 }else {  
  44.                     dataList.get(position).checked = false;  
  45.                 }  
  46.                 adapter.notifyDataSetChanged();  
  47.             }  
  48.         });  
  49.           
  50.         //全选按钮按钮设置  
  51.         Button all_sel = (Button) findViewById(R.id.all_sel);  
  52.         Button all_unsel = (Button) findViewById(R.id.all_unsel);  
  53.         all_sel.setOnClickListener(new View.OnClickListener() {  
  54.   
  55.             @Override  
  56.             public void onClick(View v) {  
  57.                 // TODO Auto-generated method stub  
  58.                 for (int i = 0; i < dataList.size(); i++) {  
  59.   
  60.                     dataList.get(i).checked=true;  
  61.                 }  
  62.                   
  63.                 adapter.notifyDataSetChanged();  
  64.             }  
  65.         });  
  66.   
  67.         //全部取消的设置  
  68.         all_unsel.setOnClickListener(new View.OnClickListener() {  
  69.   
  70.             @Override  
  71.             public void onClick(View v) {  
  72.                 // TODO Auto-generated method stub  
  73.                 for (int i = 0; i < dataList.size(); i++) {  
  74.                     dataList.get(i).checked=false;  
  75.                 }  
  76.                 adapter.notifyDataSetChanged();  
  77.             }  
  78.         });  
  79.   
  80.     }  
  81.   
  82. }  
首先是要构造我们要显示数据,下面这段代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //初始化数据  
  2. final List<DataHolder> dataList = new ArrayList<DataHolder>();  
  3. for (int i = 0; i < 10; i++) {  
  4.     dataList.add(new DataHolder("harvic的blog------" + i, "harvic",  
  5.             false));  
  6. }  
然后把构造的List<DataHolder> dataList传入ListitemAdapter的构造函数,构造Adapter,并设置到ListView中

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //构造Adapter  
  2. final ListitemAdapter adapter = new ListitemAdapter(MainActivity.this,dataList);  
  3. //设置adapter  
  4. final ListView listView = (ListView) findViewById(R.id.list);  
  5. listView.setAdapter(adapter);  
虽然我们已经为listview设置了adapter,此时已经能显示我们传进去的所有数据,但还没有办法响应我们的点击事件,所有要对ListView设置onItemClickListener

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //设置ListItem点击监听函数  
  2. listView.setOnItemClickListener(new OnItemClickListener() {  
  3.   
  4.     @Override  
  5.     public void onItemClick(AdapterView<?> parent, View view,  
  6.             int position, long id) {  
  7.         // TODO Auto-generated method stub  
  8.         boolean checked = dataList.get(position).checked;  
  9.         if (!checked) {  
  10.             dataList.get(position).checked = true;  
  11.         }else {  
  12.             dataList.get(position).checked = false;  
  13.         }  
  14.         adapter.notifyDataSetChanged();  
  15.     }  
  16. });  
当用户点击某一项时,先看当前项是选中状态还是未选中状态,如果已经选中则取消,如果没选中就选中它。最后通过adapter.notifyDataSetChanged();通知ListView显示数据已经改变,让界面根据最新数据重绘。
到这里,对于数据的显示和单击事件的响应都已经讲完了,下面就讲讲全选和全部取消按钮的实现。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //全选按钮按钮设置  
  2. Button all_sel = (Button) findViewById(R.id.all_sel);  
  3. Button all_unsel = (Button) findViewById(R.id.all_unsel);  
  4. all_sel.setOnClickListener(new View.OnClickListener() {  
  5.   
  6.     @Override  
  7.     public void onClick(View v) {  
  8.         // TODO Auto-generated method stub  
  9.         for (int i = 0; i < dataList.size(); i++) {  
  10.   
  11.             dataList.get(i).checked=true;  
  12.         }  
  13.           
  14.         adapter.notifyDataSetChanged();  
  15.     }  
  16. });  
当用户点击全选按钮时,对datalist中的所有checked数据进行轮询,将所有的checked都设为true,在listItemAdapter根据datalist中的checked字段进行显示时,自然就全部呈现选中状态。

同理,在全部取消的按钮实现时就是把dataList中表示所有Item 的checkbox选中与否状态的checked字段置为false;代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //全部取消的设置  
  2. all_unsel.setOnClickListener(new View.OnClickListener() {  
  3.   
  4.     @Override  
  5.     public void onClick(View v) {  
  6.         // TODO Auto-generated method stub  
  7.         for (int i = 0; i < dataList.size(); i++) {  
  8.             dataList.get(i).checked=false;  
  9.         }  
  10.         adapter.notifyDataSetChanged();  
  11.     }  
  12. });  

至此,整篇文章就讲完了,以前也写过几篇有关ListView的文章,当时觉得写的还挺好的,可到现在一看,完全不行,自己都不愿意再看,晦涩难懂,也可能是当时的技术不到家吧,有时间我可能会把有关ListView入门的一些东东讲给大家听,这里不再列以前的几篇文章了。


如果我的文章有帮到你,记得加关注哦。

源码地址:http://download.csdn.net/detail/harvic880925/8081811


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值