ListView的View回收引起的checkbox状态改变监听等问题解决方案

时间:2023-07-26 17:33:45 

之前讲到了自定义Adapter传递给ListView时,因为ListView的View回收,需要注意当ListView列表项中包含有带有状态标识控件的问题。详情可见之前发的帖[url=自定义Adapter实现ListView带多选框等状态控件的注意事项 https://www.jb51.net/article/33425.htm
还是这个问题,讲一个我遇到的因为两行代码位置相反引起的问题。
我的ListView中每行View包含一个ImageView、TextView、CheckBox。当ListView中有一个或一个一行CheckBox被选中就让ListView上面的Button显示,否则就隐藏。因此,需要对每行View中的CheckBox设置监听。我使用CheckBox中的OnCheckedChangeListener * ,当CheckBox的状态发生改变的时候就会触发这个 * 。先看下我自定义给ListView的Adapter的getView方法中的一些关键代码:
这是getView方法中使用到的内部类:


static class ViewHolder {
public ImageView imageView;
public TextView textView;
public CheckBox checkBox;
}


这是getView方法中利用ListView回收机制循环利用View的代码:


public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.searchitem, null);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView) convertView
.findViewById(R.id.searchitemimage);
viewHolder.textView = (TextView) convertView
.findViewById(R.id.searchitemtext);
viewHolder.checkBox = (CheckBox) convertView
.findViewById(R.id.searchitemcheckbox);
convertView.setTag(viewHolder);
} else {
// Log.i(CodeUtils.SEARCHTAG, "view is reuse");
viewHolder = (ViewHolder) convertView.getTag();
}


接下来是对其中checkbox设置显示状态和 * 的代码:


viewHolder.checkBox
.setOnCheckedChangeListener(new SearchItemOnCheckedChangeListener(
position, state));
viewHolder.checkBox.setChecked(state[position]);


之前说过了,因为ListView的回收,需要使用一个数组或list来记录每项数据中checkbox的状态。这里,state是与ListView列表等长的boolean数组,用于记录每个position(也就是每个列表项数据的id)标识的数据上checkbox应该显示的状态,初始的状态都是false。构造checkbox * 的时候需要传递当前View的position,以及整个列表checkbox的状态数组state。以下是checkBox状态改变 * 的代码:


public class SearchItemOnCheckedChangeListener implements
OnCheckedChangeListener {
private int id;
private Boolean[] state;
public SearchItemOnCheckedChangeListener(int id, Boolean[] state) {
this.id = id;
this.state = state;
}
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
state[id] = isChecked;
if (isChecked) {
checkedCount++;
}else{
checkedCount--;
}
if (checkCoutn>0) {
searchButton.setVisibility(Button.INVISIBLE);
} else {
searchButton.setVisibility(Button.VISIBLE);
}
}
}
}


这里面checkedCount初始值为0的整型,用于记录被选中多选框的数量。searchButton是根据checkbox而决定显示还是隐藏的按钮。

以上整个逻辑功能的实现代码。开头说了,这是一个我因为ListView的回收机制和两行代码位置相反引起的问题。两行代码的位置相反将导致完全不同的结果,所指的就是设置checkbox * 和状态的两行代码,起初我的顺序为:


viewHolder.checkBox.setChecked(state[position]);
viewHolder.checkBox.setOnCheckedChangeListener(new SearchItemOnCheckedChangeListener(position, state));


这样的顺序出现的问题是,当我拉动列表后,因为拉动被隐藏的列表项状态将被更改为false。这很不可思议,因为我已经分离了一个状态数组来记录每个checkbox的状态,想来想去只有一个可能,就是状态数组中的值改变了,而改变状态数组的值位置就在于OnCheckedChangeListener中。Debug了几个小时,才想通了问题就在于这两行代码为位置顺序。

起因还是得讲到ListView的回收机制。假如我的ListView最多只能显示10个View,那么起初就会调用十次getView构造十个全新的View(包括对其中的checkbox设置 * )。当我将列表往下拉出现第11个列表项的时候,顶部第一个列表项被隐藏,同样会再调用一次getView,不过此时getView的参数将返回刚刚被隐藏的第一个列表项的View,并对这个View更改数据作为即将出现的第11个View。问题就出在这里,我把checkbox.setChecked()方法调用放在了设置 * 前面,此时因为更改了checkbox的状态,势必引起触发状态更改的 * 。注意!由于第11个View是用被隐藏的第1个View回收来的,虽然还没有执行下一行设置 * 的代码,但实际上它已经拥有了一个状态 * ,这个 * 是这个View还是作为第一个View时设置。那个时候的 * 设置更改的第一项的数据,而不是第11项数据。因此,理所当然不能正确更改第11项数据,反而更改了无辜的第1项数据。如果我把两行代码顺序反过来,先更改 * ,再设置状态,引发的 * 自然也就是新的 * ,逻辑也就对了。

标签:ListView,View回收,checkbox状态
0
投稿

猜你喜欢

  • Android Java try catch 失效问题及解决

    2023-06-17 17:07:33
  • 简单了解java自定义和自然排序

    2022-01-29 02:47:26
  • 详解在LINUX上部署带有JAR包的JAVA项目

    2022-12-30 21:07:14
  • java IO流文件的读写具体实例

    2023-08-21 04:44:34
  • Android自定义定时闹钟开发

    2023-07-07 00:18:01
  • Android通过交互实现贝塞尔曲线的绘制

    2022-10-06 05:04:08
  • Mybatis 动态SQL的几种实现方法

    2023-11-10 12:15:15
  • MyBatis图文并茂讲解注解开发一对一查询

    2023-12-07 11:01:18
  • Kotlin协程launch启动流程原理详解

    2021-10-31 15:47:22
  • 关于Jedis的用法以及Jedis使用Redis事务

    2023-06-28 07:22:56
  • Android Studio使用小技巧:布局预览时填充数据

    2021-06-04 09:00:03
  • C#端口转发用法详解

    2022-09-05 08:47:08
  • Iconfont(矢量图标)+iconmoon(图标svg互转)配合javascript实现社交分享系统

    2023-09-28 13:23:19
  • Unity Shader实现径向模糊效果

    2021-09-06 01:02:47
  • android主线程和子线程之间消息传递详解

    2021-11-06 05:06:23
  • ListView通用泛型适配器

    2022-06-30 11:52:04
  • Vue结合Springboot实现用户列表单页面(前后端分离)

    2023-05-28 12:51:22
  • SpringBoot 将配置文件挂到 jar 包外面的操作方法

    2023-11-17 11:09:32
  • Android Fragment动态创建详解及示例代码

    2023-04-26 00:25:33
  • java ssm框架实现分页功能的示例代码(oracle)

    2021-10-31 01:14:40
  • asp之家 软件编程 m.aspxhome.com