ListView+CheckBox两种解决方案及原因分析

最近在用ListView+CheckBox搞一个item选中的项目,我将CheckBox的focus设置为false,另我大喜的是,CheckBox竟然可以选中(窃喜中),这么简单就搞定了,因为数据量较小,也没有发现什么问题。

后来数据多了, 页面需要滑动了, 发现了一个奇怪的问题,前面明明选中了,而再次滑动回去的时候竟然变成未选中状态!

这是我刚开始写的那段错误的代码:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
	final ViewHolder holder;
	if (convertView == null) {
		convertView = View.inflate(App.sContext, R.layout.our_sp_item, null);
		holder = new ViewHolder();
		holder.checked = (CheckBox) convertView.findViewById(R.id.cb_our_sp_checked);
		convertView.setTag(holder);
	} else {
		holder = (ViewHolder) convertView.getTag();
	}

	if(isBatchMode) {
		holder.checked.setVisibility(View.VISIBLE);
		holder.checked.setChecked(mTasks.get(position).isChecked());
	}else {
		holder.checked.setVisibility(View.GONE);
	}

	final int pos = position;
	holder.checked.setOnCheckedChangeListener(new OnCheckedChangeListener() {
		@Override
		public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
		mTasks.get(pos).setChecked(isChecked);
			}
	});

	return convertView;
}

原因分析

关于错误的原因,我们先来分析一下, 哦,对了,在分析之前,先来看看我的上一篇博客:从一次意外开始说java匿名内部类, 相信对你理解很有帮助。

下面我们开始分析出现上述状况的原因, 怎么分析呢? 跟着操作和代码走。

当我们滑动到第一条数据不再显示开始显示下面的数据的时候,因为复用了convertView, 也就是当前item的和第一条item共用了第一条convertView,这时看代码15行,setChecked修改了CheckBox的状态,而此时此刻肯定会去回调OnCheckedChangeListener,但是我们并没有给checkBox设置新的OnCheckedChangeListener, 也就是说,此时还是执行的以前的回调,但是我们在匿名类中使用了外部方法的变量,鉴于在博客:从一次意外开始说java匿名内部类 所说的,此时的pos参数还是第一条item的pos,我们第一条数据的check状态就这么被操蛋的修改了。

解决方案

我们提供两种解决方案。

第一种,我们不使用setOnCheckedChangeListener的方式去修改保存的状态,而是换用Click

@Override
public View getView(int position, View convertView, ViewGroup parent) {
	final ViewHolder holder;
	if (convertView == null) {
		convertView = View.inflate(App.sContext, R.layout.our_sp_item, null);
		holder = new ViewHolder();
		holder.checked = (CheckBox) convertView.findViewById(R.id.cb_our_sp_checked);
		convertView.setTag(holder);
	} else {
		holder = (ViewHolder) convertView.getTag();
	}

	if(isBatchMode) {
		holder.checked.setVisibility(View.VISIBLE);
		holder.checked.setChecked(mTasks.get(position).isChecked());
	}else {
		holder.checked.setVisibility(View.GONE);
	}

	final int pos = position;
	holder.checked.setOnClickListener(new OnClickListener() {
		@Override
		public void onClick(View v) {
			mTasks.get(pos).changeChecked();
		}
	});

	return convertView;
}

这种方式为什么能解决呢? 很简单,setChecked后,我们并没有提供Listener去修改值,代码也就没有了偷偷去修改的机会了。

第二种,上面分析原因也说了,主要的原因还是在setChecked之前我们并没有设置新的Listener,那好办,我们把setOnCheckedChangeListener放到setChecked之前不就解决了嘛。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
	final ViewHolder holder;
	if (convertView == null) {
		convertView = View.inflate(App.sContext, R.layout.our_sp_item, null);
		holder = new ViewHolder();
		holder.checked = (CheckBox) convertView.findViewById(R.id.cb_our_sp_checked);
		convertView.setTag(holder);
	} else {
		holder = (ViewHolder) convertView.getTag();
	}

	final int pos = position;
	holder.checked.setOnCheckedChangeListener(new OnCheckedChangeListener() {
		@Override
		public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
			mTasks.get(pos).setChecked(isChecked);
		}
	});

	if(isBatchMode) {
		holder.checked.setVisibility(View.VISIBLE);
		holder.checked.setChecked(mTasks.get(position).isChecked());
	}else {
		holder.checked.setVisibility(View.GONE);
	}

	return convertView;
}

ok,这两种方式都可以解决这个问题,各位客官喜欢哪个自己去挑,原因呢,我们也解析了,如果你对从一次意外开始说java匿名内部类 能充分的理解,那肯定是恍然大悟的,如果你还是不太明白,建议你再去看看前面一篇博客。

时间: 2024-10-26 22:27:02

ListView+CheckBox两种解决方案及原因分析的相关文章

ListView+CheckBox两种解决方式及原因分析

近期在用ListView+CheckBox搞一个item选中的项目,我将CheckBox的focus设置为false,另我大喜的是,CheckBox居然能够选中(窃喜中),这么简单就搞定了,由于数据量较小,也没有发现什么问题. 后来数据多了. 页面须要滑动了, 发现了一个奇怪的问题,前面明明选中了,而再次滑动回去的时候居然变成未选中状态! 这是我刚開始写的那段错误的代码: @Override public View getView(int position, View convertView,

ios7 自定义UINavigationBar UIBarButtonItem 10px的偏移纠正的两种解决方案

demo下载地址:http://pan.baidu.com/s/1c0eROkS 方案一:利用UINavigationBar 自带的布局item(Fixed space Bar Button Item)来适配ios7. 其实就是在原来返回按钮的位置上在多加一个占位的Item 为UINavigationBar 写一个分类.UINavigationItem+correct_offset.h #import <UIKit/UIKit.h> @interface UINavigationItem (c

创建TabHost的两种方式的简单分析

最近做了一个TabHost的界面,在做的过程中发现了一些问题,故和大家分享一下. 首先我的界面如下: 目前就我所知,创建TabHost有两种方式,第一种是继承TabActivity类,然后用getTabHost方法来得到一个TabHost的实例,然后就可以给这个TabHost添加Tab了.示例代码如下: [java] view plaincopy public class PlotHost extends TabActivity  { @Override protected void onCre

前端页面a标签嵌套a标签效果的两种解决方案

这是由工作中的一个小改动需求得到的这个解决方案的:那个需求是这样的,如图: 需求原来是球队名字没有点击功能的,而蓝色方框两队之间的比赛点击的时候会跳转到比赛文字直播页面.现在需要要求点击球队名字要跳转到球队资料库页面. 这样就会产生了a标签的嵌套. 但是总所周知a标签是内联元素(inline element),如果嵌套a标签的话浏览器则会解析成: 1 <!-- a标签进行嵌套的时候 --> 2 <a href="#outer">outerA 3 <a hr

Ajax保留浏览器历史的两种解决方案(Hash&amp;Pjax)

总是在github down点东西,github整个界面做的不错,体验也很好~对于其中的源代码滑动的特效最为喜欢了~刚开始以为这个只是普通的ajax请求效果,但是发现这个特效能够导致浏览器地址栏跟随变化,并且再点击前进后退按钮后又可以将代码滑回滑出~~于是乎就来研究下吧~ 一.通过锚点Hash实现: 在这方面其实国内很早就有做了,比如淘宝画报,通过的是在地址栏后面加#锚点实现的,浏览器是可以识别锚点为单位的历史记录的.但不是说页面本身有这个锚点,锚点的Hash只是起到一个引导浏览器将这次的记录推

window.onload绑定多个事件 —— 两种解决方案

前言 有些函数,必须在网页加载完毕后执行.比如:涉及DOM操作的. 网页加载完毕时会触发一个onload事件,将函数绑定到这个事件上即可. window.onload = myFunction; 问题来了:如果需要同时绑定多个事件,该如何处理呢?有两种解决方法 方案一 创建一个匿名函数,来容纳需要绑定的多个事件,再讲这个匿名函数绑定到onload事件上 1 window.onload = function(){ 2 firstFunction(); 3 secondFunction(); 4 .

openstack中运行定时任务的两种方法及源代码分析

启动一个进程,如要想要这个进程的某个方法定时得进行执行的话,在openstack有两种方式: 一种是通过继承 periodic_task.PeriodicTasks,另一种是使用loopingcall.py,针对两种方式分别说一下实现原理. (1) 继承periodic_task.PeriodicTasks 这种方式比较复杂,用到了python中的一些比较高级的特性,装饰器和元类:首先看一下periodic_task.py,在nova/openstack/common中,其他组件也有. 看一下P

sql server中批量插入与更新两种解决方案分享

若只是需要大批量插入数据使用bcp是最好的,若同时需要插入.删除.更新建议使用SqlDataAdapter我测试过有很高的效率,一般情况下这两种就满足需求了 bcp方式 复制代码 代码如下: /// <summary> /// 大批量插入数据(2000每批次) /// 已采用整体事物控制 /// </summary> /// <param name="connString">数据库链接字符串</param> /// <param n

USB设备(移动硬盘、鼠标)掉电掉驱动的两种解决方案

症状: 当你发现"移动硬盘图标"经常无故消失,又自己出现时. 你可以把这个现象称之为"掉电" or "掉驱动". 遇到这种情况,相当不爽. 比如"拷贝大文件"快完成的时候,掉之,那就真的是"我去...". 这种现象在笔记本上尤为常见,台式机相对比较少. 怎么解决呢? 解决方案: 1.取消勾选"允许计算机关闭此设备以节约电源(A)". 从"我的电脑" or "