带checkbox的ListView实现(三)——CheckableImageView的实现方法

前言:看来总结的速度还是太慢了,要写博客的内容列表已经排到了六篇,但并不想为了写博客而写博客,还是想给大家推出高品质的博客,能尽量写的详细一点,工作上也是忙,只有每天晚上抽时间写出来。这篇文章是大家在实际开发中都会遇到的问题——如何实现checkableImageview,虽然大家用其它替代方法也能实现类似效果,但我觉得只有真正实现checkable接口的自定义控件才够正宗。下面在第二篇的基本上,改进工程;

 

相关文章:

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

 

本文效果:

本篇文章建立在第二篇《带checkbox的ListView实现(二)——自定义Checkable控件的实现方法》的基础之上,建议先把这第二篇先看一遍,因为里面涉及到整体架构和CheckableFrameLayout的实现,如果不看的话,估计这一篇是看不大懂的。

 

好啦,言规正转,当大家看完第二篇,应该会想,这一篇还不简单,直接仿照CheckableFrameLayout重新实现一个自定义控件呗。好,那我们就先仿照CheckableFrameLayout实现一个看看。

 

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

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.checkableimageview.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>

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

先看看布局代码:

 

 

<?xml version="1.0" encoding="utf-8"?>
<com.harvic.checkableimageview.CheckableFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="68dp" >

    <com.harvic.checkableimageview.CheckableImageView
        android:layout_gravity="right|center_vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="10dp"
        android:clickable="false"
        android:src="@drawable/toggle" />

    <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:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="12sp" />
    </LinearLayout>

</com.harvic.checkableimageview.CheckableFrameLayout>

这里除了使用CheckableFrameLayout,另外把第二篇中的Checkbox换成了自定义的CheckableImageView,其实整篇文章跟第二篇相比,什么都没有变,只是这里把checkbox换成了自定义的CheckableImageView。
我单独把CheckableImageView的代码拿出来:

 

 

    <com.harvic.checkableimageview.CheckableImageView
        android:layout_gravity="right|center_vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="10dp"
        android:clickable="false"
        android:src="@drawable/toggle" />

因为我们要让它显示图片的不同状态,所以CheckableImageVIew应该派生于ImageVIew,也就是说它是一个ImageView的派生类,所以它具有src属性,它指向听toggle.xml是一个selector,在不同状态下选择不同的图片显示:

 

 

<selector android:constantSize="true" android:variablePadding="false" xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/toggle_on"  android:state_checked="true"/>
    <item android:drawable="@drawable/toggle_off" android:state_checked="false"/>
    <item android:drawable="@drawable/toggle_off"/>
</selector>

好啦,下面就是CheckableImageView的实现了

 

我们仿照CheckableFrameLayout实现Checkable接口,又因为CheckableImageView要显示图片,所以又要派生自ImageView,所以代码如下:

 

public class CheckableImageView extends ImageView implements Checkable{

	/** @param context
	 * @param attrs */
	public CheckableImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}
	
	private boolean mChecked = false;
	@Override
	public void setChecked(boolean checked) {
		// TODO Auto-generated method stub
		if (mChecked != checked) {
			mChecked = checked;
			refreshDrawableState();
		}
	}

	@Override
	public boolean isChecked() {
		// TODO Auto-generated method stub
		return mChecked;
	}

	@Override
	public void toggle() {
		// TODO Auto-generated method stub
		setChecked(!mChecked);
	}

}

其它部分(MainActivity、ListItemAdapter)都不变,这里就不再列代码了,没什么意义,看看效果:

 


没效果!!!!!为什么会这样!!!!!!!!
放用户点击某个Item的时候,我们明明设置了SetChecked()了啊,在SetChecked()内部:

 

public void setChecked(boolean checked) {
	// TODO Auto-generated method stub
	if (mChecked != checked) {
		mChecked = checked;
		refreshDrawableState();
	}
}

会将状态设为checked并且调用refreshDrawableState()来刷新Drawable状态的啊,在刷新状态时,会调用我们的Selector:

 

 

<item android:drawable="@drawable/toggle_on"  android:state_checked="true"/>

注意!!!!!这里有个问题!!!!!我们的ImageView控件并不像Checkbox一样有state_cheked(选中)状态,所以我们给他设置android:state_checked="true"标签让它自已匹配选中状态是没用的!!!!!因为它根本没有选中状态。那问题来了,我们怎么样能让ImageView具有选中状态呢。当他有了选中状态以后,那么state_checked标签就有用了。所以下面我们就想方设法让它具有选中状态。
首先,我们可以重写onCreateDrawableState函数,来指定控件所具有的状态。即当控件在获取自己所应具有的状态时,会调用onCreateDrawableState(int extraSpace)方法,其定义如下:

 

 

 

 

public int[] onCreateDrawableState(int extraSpace){
}

int extraSpace:代表额外的空间,即如果为0,即代表用户不再另外指定属性,直接返回本身所具有的控件状态集。如果不为0,多余的位置就可以用来放置自定义的状态,使该Drawable具有我们添加的属性。

 

返回值:如果extraSpace为0,直接返回控件本身所具有的状态集。如果extraSpace不为0,返回的值除了具有控件本身所具有的状态集,会额外分配指定的空间以便用户自主指定状态。

下面看看我们重写onCreateDrawableState()的完整代码:

 

int[] CHECKED_STATE_SET = { android.R.attr.state_checked };

android.R.attr.state_checked:指示控件已经选中时的状态,如果给控件指定这个属性,就代表控件就已经选中,会调用selector里的android:state_checked="true"来加载它的显示图片。

 

 

@Override
public int[] onCreateDrawableState(int extraSpace) {
	final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
	if (isChecked()) {
		mergeDrawableStates(drawableState, CHECKED_STATE_SET);
	}
	return drawableState;
}

首先:

 

 

final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);

获取控件本身所具有的状态,并且我们要另开一个位置存放android.R.attr.state_checked状态,所以要加1;

 

然后,当控件被选中的时候,我们要给它添加上被选中的状态,让它调用selector里的android:state_checked="true"来加载当前选中时的图片。

所以完整的代码应该是这样的:

 

public class CheckableImageView extends ImageView implements Checkable{

	/** @param context
	 * @param attrs */
	public CheckableImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}
	
	private boolean mChecked = false;
	private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };


	@Override
	public int[] onCreateDrawableState(int extraSpace) {
		final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
		if (isChecked()) {
			mergeDrawableStates(drawableState, CHECKED_STATE_SET);
		}
		return drawableState;
	}
	
	@Override
	public void setChecked(boolean checked) {
		// TODO Auto-generated method stub
		if (mChecked != checked) {
			mChecked = checked;
			refreshDrawableState();
		}
	}

	@Override
	public boolean isChecked() {
		// TODO Auto-generated method stub
		return mChecked;
	}

	@Override
	public void toggle() {
		// TODO Auto-generated method stub
		setChecked(!mChecked);
	}

}

附:

 

android.R.attr.state_checked 的官方说明是这样的:
State identifier indicating that the object is currently checked. See state_checkable for an additional identifier that can indicate if any object may ever display a check,regardless of whether state_checked is currently set. Must be a boolean value, either "true" or "false". This may also be a reference to a resource (in the form "@[package:]type:name") or  theme attribute (in the form "?[package:][type:]name") containing a value of this type. 

CheckableImageView总结:

(1)CheckableImageView执行流程:setChecked -> refreshDrawableState ->onCreateDrawableState ->调用selector设置状态

(2)CheckableImageView派生自Checkable接口,使CheckableImageView具有的可选中的状态,但当用户点击选中时,我们感觉会调用selector的state_checked="true"的状态,但state_checked属性对ImageView是无效的,因为ImageView并不具有可选中的属性,所以我们在调用selector前,应该为ImageView添加上state_checked属性这就是重写onCreateDrawableState()的原因,当用户选中时,给ImageView设置选中状态,强制使它处于选中状态中,这时,它就会调用我们的state_checked属性

OK啦,本文就到了,这里只列出来了改变的部分,大家参考源码看一下吧。

 

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

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

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

 

如果你喜欢我的文章,你可能更喜欢我的公众号

启舰杂谈

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值