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) 收藏 ...
随机推荐
- [K/3Cloud] 表单python脚本使用QueryService的做法
听说有些朋友想在表单里做自定义的界面数据处理,一般来说写个表单插件会很容易解决这类问题.但是鉴于C#插件开发的不便性和实施搭建开发环境的麻烦,在现场做C#开发可能会不太方便(没部署开发环境之类的问题) ...
- IBOutlet loadView UIButton的subview数量 UIWebView
IBOutlet声明的插座变量和属性一起使用的时候,在.m文件调用的是属性. 在loadView方法中获取view属性会产生循环引用问题并导致内存溢出. Control+E到行尾,Control+A到 ...
- 前段集成解决方案grunt+yeoman初步认识
1.什么是前段集成解决方案? 将前端研发领域中各种分散的技术元素集中在一起,并对常见的前端开发问题.不足.缺陷和需求,所提出的一种解决问题的方案 2.yeoman 应用的架构,模型! 相当于一个生成 ...
- HDOJ 4705 Y 树形DP
DP:求出3点构成链的方案数 .然后总方案数减去它 Y Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K ...
- mac 下安装caffe(一)
1.brew install --build-from-source -vd boost boost-python 这一步出错:libtool: unrecognized option `-stati ...
- HDU5501/BestCoder Round #59 (div.2)The Highest Mark dp+贪心
The Highest Mark 问题描述 2045年的SD省队选拔,赛制和三十年前已是完全不同.一场比赛的比赛时间有 tt 分钟,有 nn 道题目. 第 ii 道题目的初始分值为 A_i(A_i \ ...
- CMDBuild安装及webservice接口的获取
近期项目组之前一直使用的OneCMDB出现了问题,在增删改数据时异常的慢.于是考虑能否够优化OneCMDB.由于本人水平有限,对OneCMDB进行代码级别的优化临时还有点难度.于是就对现有的其它开源C ...
- Hybrid 开发
主讲人:吴彬 要学习某个东西之前,我们首先要了解这个东西是什么?然后我们要了解这东西有什么用,有什么好处和弊端?最后我们要知道这东西怎么用? 简单点就是 ——是什么?有什么用?怎么用? 那么进入正题 ...
- Android SDK Manager 无法更新问题(转载)
先看看如何加快更新速度,再说如何更新. 首先更新host文件,如图,打开目录 C:\Windows\System32\drivers\etc,在目录下有hosts文件 打开方式选用“记事本”打开 将一 ...
- 无限极分类算法 thinkphp
<?php/** 本类实现的是无限级递归分类的管理*/class InfiniteLevel{ public $id_str=""; public function add_ ...