Android RecyclerView遇到notifyDataSetChanged无效时的解决方案
一、简述
不管AbsListView(ListView、GridView)或是新出的RecyclerView,在使用notifyDataSetChanged方法更新列表数据时,一定要保证数据为同个对象(即hashCode要一致)。对于这个问题的论证,可以去看官方源代码,或是看我之前写的一篇博文“解决ListViews适配器notifyDataSetChanged()无效问题”,相信可以帮到你。但是,这个不是本文的重点,本文重点讲解在Fragment中,RecyclerView遇到notifyDataSetChanged无效的问题。如果你赶时间,可以直接看第三部分(”总结”)。
二、探索
1、查看数据(mData)是否是同个对象
*tip:java中可以通过打印hashCode的方式判断mData是否为同个对象。
注意:initData方法在onActivityCreated()中被调用。
public void initData() {
if (mData == null) {
mData = new ArrayList<>();
}
mData.clear();
...
数据填充
...
if (mAdapter == null) {
mAdapter = new LQRAdapterForRecyclerView<String>(getActivity(), mData, R.layout.item_senior) {
@Override
public void convert(LQRViewHolderForRecyclerView helper, String item, int position) {
...
视图填充
...
}
};
mRvList.setAdapter(mAdapter);
LogUtils.sf("setAdapter时mData地址:" + mData.hashCode());
} else {
mAdapter.notifyDataSetChanged();
LogUtils.sf("setAdapter时mData地址:" + mData.hashCode());
}
}
2、操作与结果
*tip:常规对Fragment的使用,会对其进行缓存,也可能使用单例模式,反正就是短时间内不会重新创建。
①操作一:
打开Activity后,切换Fragment(第一次初始化Fragment)。显示效果如下:
②操作二:
切换别的Fragment后,再切回刚才的Fragment(此前该Fragment已经在存在,所以不会再次创建)。显示效果如下:
③看控制台:
可以看到数据对象地址一样,即为同一个。
3、查看RecyclerView是否是同个对象
说实话,这个是踩坑经验丰富的网友在群里说的,如果不是他说出来,打死我也没想到,居然还有这么一个坑。从上面的结果可以看出,adapter中是有数据的没错,而且数据地址没变,所以理应notifyDataSetChanged()方法会生效。但是为什么会这样呢,这里先卖个关子,先看下面的操作。
①改下上面的代码,打印RecyclerView的地址。
代码如下:
public void initData() {
if (mData == null) {
mData = new ArrayList<>();
}
mData.clear();
...
数据填充
...
if (mAdapter == null) {
mAdapter = new LQRAdapterForRecyclerView<String>(getActivity(), mData, R.layout.item_senior) {
@Override
public void convert(LQRViewHolderForRecyclerView helper, String item, int position) {
...
视图填充
...
}
};
mRvList.setAdapter(mAdapter);
LogUtils.sf("setAdapter时Rv:" + mRvList.hashCode());
} else {
mAdapter.notifyDataSetChanged();
LogUtils.sf("notify时Rv:" + mRvList.hashCode());
}
}
②同上述操作一致。
对同一个Fragment来回切换,看控制台输出。
果然不一样!!!
三、总结
为什么在Fragment中RecyclerView的地址会发生变化呢?我们先理清一下Fragment生命周期会陆续调用的几个方法:
onCreate() -> onCreateView() -> onActivityCreated() -> onDestroy()
中间少了几个方法,请不用在意,下面贴下我的BaseFragment代码:
public abstract class BaseFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//子类不再需要设置布局ID,也不再需要使用ButterKnife.bind()
View rootView = inflater.inflate(provideContentViewId(), container, false);
ButterKnife.bind(this, rootView);
initView(rootView);
return rootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initData();
initListener();
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
当一个Fragment在来回切换时,分别调用的方法如下:
第一次显示:
onCreate() -> onCreateView() -> onActivityCreated()
第二次显示:
onCreateView() -> onActivityCreated()
这里不难理解,因为Fragment一般使用的时候会被缓存,所以,当第二次显示的时候,不会调用onCreate()。只会调用onCreateView()和onActivityCreated(),这也就是RecyclerView地址不一样的原因所在,因为控件获取操作是在initView()中进行的,即RecyclerView的获取操作在onCreateView()中,而Fragment的每次显示都会调用onCreateView(),所以RecyclerView控件会被再次获取,即重新创建一个对象(此时hashCode就变化了)。
1、结论:
所以,在Fragment中使用RecyclerView或AbsListView控件的notifyDataSetChanged()方法时,除了保证数据(mData对象)不能变以外,控件本身一样也不能变。
2、解决方案:
1)方案一:
因为Fragment的onCreateView()和onActivityCreated()方法在每次Fragment显示的时候会被调用,控件会被重新创建一次,所以,解决方法只能是在这两个方法中重新对RecyclerView设置适配器,而不要使用notifyDataSetChanged(),故代码改为如下:
public void initData() {
if (mData == null) {
mData = new ArrayList<>();
}
mData.clear();
...
数据填充
...
if (mAdapter == null) {
mAdapter = new LQRAdapterForRecyclerView<String>(getActivity(), mData, R.layout.item_senior) {
@Override
public void convert(LQRViewHolderForRecyclerView helper, String item, int position) {
...
视图填充
...
}
};
}
mRvList.setAdapter(mAdapter);
}
注:只是建议不要在上述两个生命周期方法中使用notifyDataSetChanged()而已,只要在保证RecyclerView等列表控件设置完适配器后,可以在任意地方继续使用notifyDataSetChanged()。
2)方案二:
让rootView作为全局变量,在回调onCreateView()时不再重新创建。
public abstract class BaseFragment extends Fragment {
View rootView;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//子类不再需要设置布局ID,也不再需要使用ButterKnife.bind()
if(rootView == null){
rootView = inflater.inflate(provideContentViewId(), container, false);
ButterKnife.bind(this, rootView);
initView(rootView);
}
return rootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initData();
initListener();
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
Android RecyclerView遇到notifyDataSetChanged无效时的解决方案的更多相关文章
- thinkphp事务处理以及无效时的解决方案(整理)
thinkphp事务处理以及无效时的解决方案(整理) 一.总结 一句话总结:要程序里面支持事务,首先连接的数据库和数据表必须支持事务 mysql 1.InnoDB和MyISAM对事务的支持怎么样? I ...
- Android RecyclerView使用 及 滑动时加载图片优化方案
1.控制线程数量 + 数据分页加载2.重写onScrollStateChanged方法 这个我们后面再谈,下面先来看看RecyclerView控件的使用及我们为什么选择使用它 RecyclerView ...
- Android RecyclerView.Adapter notifyDataSetChanged 不起作用
我在自己动手写RecyclerView的上拉加载更多,最后就差一步,这个时候数据已经加载完了,UI上面没有显示,我而且也调用了notifyDataSetChanged刷新item的数据,但是一直没效果 ...
- (转载) Scrollview 嵌套 RecyclerView 及在Android 5.1版本滑动时 惯性消失问题
Scrollview 嵌套 RecyclerView 及在Android 5.1版本滑动时 惯性消失问题 标签: scrollviewandroid滑动嵌套 2015-07-16 17:24 1112 ...
- Android 关于ListView中adapter调用notifyDataSetChanged无效的原因
话说这个问题已经困扰我很久了,一直找不到原因,我以为只要数据变了,调用adapter的notifyDataSetChanged就会更新列表,最近在做微博帐号管理这一块,想着动态更新列表,数据是变了,但 ...
- Android开发之关于ListView中adapter调用notifyDataSetChanged无效的原因
1.数据源没有更新,调用notifyDataSetChanged无效. 2.数据源更新了,但是它指向新的引用,调用notifyDataSetChanged无效. 3.数据源更新了,但是adpter没有 ...
- Android RecyclerView的基本使用
Android RecyclerView 在去年的Google I/O大会上就推出来了,以前经常使用的ListView 继承的是AbsListView,而RecyclerView则直接继承 ViewG ...
- 极简的Android RecyclerView Adapter(使用DataBinding)
阅读本篇文章需要读者对Android Databinding和RecyclerView有一定的了解. 简介 我们知道,DataBinding的核心理念是数据驱动.数据驱动驱动的目标就是View,使用D ...
- (转载) Android RecyclerView 使用完全解析 体验艺术般的控件
Android RecyclerView 使用完全解析 体验艺术般的控件 标签: Recyclerviewpager瀑布流 2015-04-16 09:07 721474人阅读 评论(458) 收藏 ...
随机推荐
- [Vue @Component] Simplify Vue Components with vue-class-component
While traditional Vue components require a data function which returns an object and a method object ...
- Xsolla带你走进印度移动游戏市场
作为世界上人口第二大国的印度.随着移动时代的到来,越来越多的开发人员将目光瞄准了这个国家.今天Xsolla就带领大家一起来了解这样一个新兴的移动游戏市场. 印度移动市场潜力巨大 作为世界四大宗教和数字 ...
- ADO.NET .net core2.0添加json文件并转化成类注入控制器使用 简单了解 iTextSharp实现HTML to PDF ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC 了解一下 C# AutoMapper 了解一下
ADO.NET 一.ADO.NET概要 ADO.NET是.NET框架中的重要组件,主要用于完成C#应用程序访问数据库 二.ADO.NET的组成 ①System.Data → DataTable, ...
- 微信公众号H5用户授权
其实不是很难,总结起来就是: 1.微信公众号管理后台设置redirect_uri. 2.然后发起一个请求去重定向获取code,然后把获取到code之后重定向的URL放在获取code的URL中 3.获取 ...
- VS2013大括号高亮显示的设置方法
在做公司一个项目的时候,需要用到VS2013编辑器,用了之后感觉有点不太爽. 在js文件里定义了方法之后,由于方法比较长,写完之后再回来看就有些不方便,尤其是方法里还有几个判断,后人再看的时候会有些不 ...
- Xamarin nuget package update 错误
update xamarin.Forms包时出现错误: 'The specified path, file name, or both are too long. The fully qualifie ...
- 【源代码剖析】tornado-memcached-sessions —— Tornado session 支持的实现(二)
客官您最终回头了! 让我们本着探(zuo)索(si)精神把 session.py 看完吧... 首先看看须要的库: pickle 一个用于序列化反序列化的库(听不懂?你直接看成和 json 一样作用即 ...
- Apache Kafka-0.8.1.1源代码编译
作者:过往记忆 | 新浪微博:左手牵右手TEL | 能够转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明博客地址:http://www.iteblog.com/文章标题:<Apac ...
- 【网易云音乐 for linux】 踩过的坑
1.从官网下载的包,却怎么也安装不上. 提示依赖,网上全是什么 sudo apt-get -f install ,结果提示有没有完成安装的包,让我卸载. 于是按下Y卸载了网易云. 尝试一个个修复以来, ...
- python pass 的用法
python pass用法 1.空语句 do nothing 2.保证格式完整 3.保证语义完整 4.以if语句为例: C/C++中写法: if(true) ; // do nothing else ...