IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
最近在使用RecyclerViewAdapter的时候遇到这个问题,这里总结记录一下,先看一下报错日志:
原因是我在Adapter中的item放了一个CheckBox, 并给CheckBox设置了监听,当选中状态改变的时候,在监听回调中直接调用notifyItemChanged就会出现这个问题。代码如下:
@Override
protected void convert(final BaseViewHolder helper, ContactDataSection item) {
final ContactMobileManage mobileContact = item.getEntity();
if (mobileContact != null) {
....
CheckBox checkBox = helper.getView(R.id.ckb_check);
checkBox.setChecked(mobileContact.isChecked());
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) {
mobileContact.setChecked(isChecked);
notifyItemChanged(helper.getAdapterPosition());
}
});
}
}
我们在RecyclerView源码中找到错误日志中提到的assertNotInLayoutOrScroll
方法看一下:
/**
* Checks if RecyclerView is in the middle of a layout or scroll and throws an
* {@link IllegalStateException} if it <b>is</b>.
*
* @param message The message for the exception. Can be null.
* @see #assertInLayoutOrScroll(String)
*/
void assertNotInLayoutOrScroll(String message) {
if (isComputingLayout()) {
if (message == null) {
throw new IllegalStateException("Cannot call this method while RecyclerView is "
+ "computing a layout or scrolling" + exceptionLabel());
}
throw new IllegalStateException(message);
}
if (mDispatchScrollCounter > 0) {
Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might"
+ "be run during a measure & layout pass where you cannot change the"
+ "RecyclerView data. Any method call that might change the structure"
+ "of the RecyclerView or the adapter contents should be postponed to"
+ "the next frame.",
new IllegalStateException("" + exceptionLabel()));
}
}
可以看到第一行isComputingLayout()
方法为true的时候就会抛出这个异常,我们再看一下isComputingLayout()
方法的代码:
/**
* Returns whether RecyclerView is currently computing a layout.
* <p>
* If this method returns true, it means that RecyclerView is in a lockdown state and any
* attempt to update adapter contents will result in an exception because adapter contents
* cannot be changed while RecyclerView is trying to compute the layout.
* <p>
* It is very unlikely that your code will be running during this state as it is
* called by the framework when a layout traversal happens or RecyclerView starts to scroll
* in response to system events (touch, accessibility etc).
* <p>
* This case may happen if you have some custom logic to change adapter contents in
* response to a View callback (e.g. focus change callback) which might be triggered during a
* layout calculation. In these cases, you should just postpone the change using a Handler or a
* similar mechanism.
*
* @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
* otherwise
*/
public boolean isComputingLayout() {
return mLayoutOrScrollCounter > 0;
}
它这里的注释写的很清楚了,当这个方法返回值为true时,RecyclerView会处于锁定状态,这时任何尝试更新Adapter内容的方法都将导致异常,因为RecyclerView在计算layout的时候不允许你更新Adapter内容。
后面它又说,你的代码不太可能处于该状态下运行,因为这个状态只有系统framework调用或者响应系统事件的时候才会发生(我信你个鬼哦,可它就是发生了啊,真是shit happens…)
最后,它又补充了一句,说如果确实发生了,那有可能是在你设置了View callback的情况下(如focus change callback),这时你需要使用Handler或类似的方法进行延时处理。
好吧。。。最后它还是承认了自己确实给开发者挖了一个坑。。。不知道RecyclerView还挖了多少坑
接下来就是我们的填坑时间了
解决方法:
@Override
protected void convert(final BaseViewHolder helper, ContactDataSection item) {
final ContactMobileManage mobileContact = item.getEntity();
if (mobileContact != null) {
...
CheckBox checkBox = helper.getView(R.id.ckb_check);
checkBox.setChecked(mobileContact.isChecked());
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) {
if (mRecyclerView.isComputingLayout()) {
mRecyclerView.post(new Runnable() {
@Override
public void run() {
mobileContact.setChecked(isChecked);
notifyItemChanged(helper.getAdapterPosition());
}
});
} else {
mobileContact.setChecked(isChecked);
notifyItemChanged(helper.getAdapterPosition());
}
}
});
}
}
解决方法比较简单,既然RecyclerView已经给了判断方法了,那我们在onCheckedChanged方法中直接调用isComputingLayout()
判断一下就可以了,如果该方法返回为true我们需要进行延时处理这里我直接调用RecyclerView对象的post方法或者你用Handler处理也可以。
- 如何降级Cloudera Manager和CDH
- 如何在CDH中安装和使用StreamSets
- 如何使用Oozie API接口向非Kerberos环境的CDH集群提交Shell工作流
- 如何使用Oozie API接口向非Kerberos环境的CDH集群提交Java作业
- 如何通过Livy的RESTful API接口向Kerberos环境的CDH集群提交作业
- 如何使用Oozie API接口向非Kerberos环境的CDH集群提交Spark作业
- Joomla 权限提升漏洞(CVE-2016-9838)分析
- Firefox - SVG cross domain cookie vulnerability
- 当代 Web 的 JSON 劫持技巧
- 利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事
- Nginx权限提升漏洞(CVE-2016-1247 )分析
- 初识 Fuzzing 工具 WinAFL
- 如何使用Oozie API接口向Kerberos环境的CDH集群提交Spark2作业
- 如何编译及使用TPC-DS生成测试数据
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- php适配器模式简单应用示例
- Python 解析简单的XML数据
- Laravel 实现Eloquent模型分组查询并返回每个分组的数量 groupBy()
- Python分类测试代码实例汇总
- Swoole 5将移除自动添加Event::wait()特性详解
- laravel 数据验证规则详解
- JS操作XML中DTD介绍及使用方法分析
- PHP设计模式之迭代器(Iterator)模式入门与应用详解
- PHP FileSystem 文件系统常用api整理总结
- laravel框架之数据库查出来的对象实现转化为数组
- php apache开启跨域模式过程详解
- laravel5.6实现数值转换
- python中return不返回值的问题解析
- php装饰者模式简单应用案例分析
- php常用日期时间函数实例小结