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) 收藏 ...
随机推荐
- NYOJ 832 合并游戏
合并游戏 时间限制:1000 ms | 内存限制:65535 KB 难度:4 描述 大家都知道Yougth除了热爱编程之外,他还有一个爱好就是喜欢玩.某天在河边玩耍的时候,他发现了一种神奇的石 ...
- Android BottomSheet:便捷易用的底部滑出面板(1)
Android BottomSheet:便捷易用的底部滑出面板(1) Android BottomSheet是github上的一个第三方开源项目,其主页:https://github.com/Flip ...
- Wikioi 3776 生活大爆炸版石头剪子布
题目描述 Description 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头.如果两个人出拳一样,则不分胜负.在<生活大爆炸>第二季第8集中出现了一种石头剪刀布的升级版游戏 ...
- 在IIS6,7中部署ASP.NET网站[转]
阅读目录 开始 查看web.config文件 在IIS中创建网站 IIS6 添加扩展名映射 IIS6 无扩展名的映射 目录的写入权限 SQL SERVER的配置 在IIS7中部署ASP.NET程序 8 ...
- mybatis中jdbcType的作用和是否必须
1. mybatis中 jdbcType 时间类型 当jdbcType = DATE 时, 只传入了 年月日 jdbcType = TIMESTAMP , 年月日+ 时分秒 2. jdbcType ...
- 阿里云nginx创建多站点
最近开始用阿里云的vps,用了它的一键安装包安装了php环境,nginx的.下面记录创建多站点的心得. 首先php安装好后会自带安装一个phpwind的站点. 文件目录存放在 /alidata/www ...
- web的自己主动化公布
</pre>基于眼下业务的版本号.使用的maven 及tomcat <p></p><p>假设我们使用 Jenkins 公布是比較好的,可是存在一定的问题 ...
- UVA 11423 - Cache Simulator (树状数组)
UVA 11423 - Cache Simulator (树状数组) option=com_onlinejudge&Itemid=8&category=523&page=sho ...
- cocos2d-x 3.0 常见问题及解决
我自己遇到的问题记录,会及时更新.希望对大家有帮助 1.打包图片生成plist后,假设在游戏中图片挨在一起可能会出现黑线,打包时将Extrude设为1就可以 2.Xcode环境下.更新资源后执行的时候 ...
- ios10--拳皇动画
/** 图片的两种加载方式: 1> imageNamed: a. 就算指向它的指针被销毁,该资源也不会被从内存中干掉, b. 放到Assets.xcassets的图片,默认就有缓存, c. 图片 ...