现在的位置: 首页 > 综合 > 正文

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

2017年10月14日 ⁄ 综合 ⁄ 共 8991字 ⁄ 字号 评论关闭

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

相关文章:

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

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

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

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

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.harvic.trylistviewcheckbox.MainActivity" >

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="100dp"
        android:choiceMode="multipleChoice"
        android:dividerHeight="1px"
        android:scrollbars="none" />
    
    <Button android:id="@+id/all_sel"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="50dip"
        android:layout_gravity="bottom"
        android:text="全选"
        />
    <Button android:id="@+id/all_unsel"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:text="全部取消"/>
</FrameLayout>

注意啦:

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

在ListView中:

android:choiceMode="multipleChoice"

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

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

<?xml version="1.0" encoding="utf-8"?>

<com.harvic.trylistviewcheckbox.CheckableFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="68dp">

    <CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right|center_vertical"
        android:button="@drawable/checkbox_selector"
        android:clickable="false"
        android:focusable="false" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="17dp"
        android:layout_marginTop="17dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/subtitle"
            android:textSize="12sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

</com.harvic.trylistviewcheckbox.CheckableFrameLayout>

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

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

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

package com.harvic.trylistviewcheckbox;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Checkable;
import android.widget.FrameLayout;

public class CheckableFrameLayout extends FrameLayout implements Checkable {

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

	private boolean mChecked = false;

	@Override
	public void toggle() {
		setChecked(!mChecked);
	}

	@Override
	public boolean isChecked() {
		return mChecked;
	}

	@Override
	public void setChecked(boolean checked) {
		if (mChecked != checked) {
			mChecked = checked;
			refreshDrawableState();
			for (int i = 0, len = getChildCount(); i < len; i++) {
				View child = getChildAt(i);
				if(child instanceof Checkable){
					((Checkable) child).setChecked(checked);
				}
			}
		}
	}

}

因为我们用到的是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()函数单独拿出来看看:

public void setChecked(boolean checked) {
	if (mChecked != checked) {
		mChecked = checked;
		refreshDrawableState();
		for (int i = 0, len = getChildCount(); i < len; i++) {
			View child = getChildAt(i);
			if(child instanceof Checkable){
				((Checkable) child).setChecked(checked);
			}
		}
	}
}

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

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

public class ViewHolder{
	public TextView mTitle;
	public TextView mSubTitile;	
};

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

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

package com.harvic.trylistviewcheckboxdata;

public class DataHolder{
	public String titleStr;
	public String subTitleStr;
	
	public DataHolder(String title,String subTitle){
		titleStr = title;
		subTitleStr = subTitle;		
	}
}

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

五、ListItemAdapter

同样,先列出全部代码,然后再看看有什么不同。

package com.harvic.trylistviewcheckbox;

import java.util.List;

import com.example.trylistviewcheckbox.R;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class ListitemAdapter extends BaseAdapter {

	private List<DataHolder> mList;
	private Context mContext;
	private LayoutInflater mInflater;
	public ListitemAdapter(Context context,List<DataHolder> list){
		mList = list;
		mContext = context;
		mInflater = LayoutInflater.from(context);
	}
	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return mList.size();
	}

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

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

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		ViewHolder holder = null;  
        if (convertView == null) {  
              
            holder=new ViewHolder();    
              
            convertView = mInflater.inflate(R.layout.check_list_item, null);   
            holder.mTitle = (TextView)convertView.findViewById(R.id.title);  
            holder.mSubTitile = (TextView)convertView.findViewById(R.id.subtitle);  
            convertView.setTag(holder);  
              
        }else {  
              
            holder = (ViewHolder)convertView.getTag();  
        }  
           
        holder.mTitle.setText((String)mList.get(position).titleStr);  
        holder.mSubTitile.setText((String)mList.get(position).subTitleStr);  
        return convertView;  
	}
	
}

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

六、 MainActivity

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

package com.harvic.trylistviewcheckbox;


import java.util.ArrayList;
import java.util.List;

import com.example.trylistviewcheckbox.R;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		//构造数据
		final List<DataHolder> dataList = new ArrayList<DataHolder>();
		for(int i=0;i<10;i++){
			dataList.add(new DataHolder("harvic的blog------"+i,"harvic"));
		}
		//构造Adapter
		ListitemAdapter adapter = new ListitemAdapter(MainActivity.this, dataList);
		final ListView listView = (ListView)findViewById(R.id.list);
		listView.setAdapter(adapter);
		
		//全部选中按钮的处理
		Button all_sel = (Button)findViewById(R.id.all_sel);
		Button all_unsel = (Button)findViewById(R.id.all_unsel);
		all_sel.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				for(int i=0;i<dataList.size();i++){
					listView.setItemChecked(i, true);
				}
			}
		});
		
		//全部取消按钮处理
		all_unsel.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				for(int i=0;i<dataList.size();i++){
					listView.setItemChecked(i, false);
				}
			}
		});
	}

}

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

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

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

再看看全选按钮的实现:

all_sel.setOnClickListener(new View.OnClickListener() {
	
	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		for(int i=0;i<dataList.size();i++){
			listView.setItemChecked(i, true);
		}
	}
});

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

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

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

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

SparseBooleanArray checkedArray = mListView.getCheckedItemPositions();
for (int i = 0; i < checkedArray.size(); i++) {
    if (checkedArray.valueAt(i)){
        ……//i就是选中的行号
    }
}

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

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

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

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

抱歉!评论已关闭.