一、简述

不管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无效时的解决方案的更多相关文章

  1. thinkphp事务处理以及无效时的解决方案(整理)

    thinkphp事务处理以及无效时的解决方案(整理) 一.总结 一句话总结:要程序里面支持事务,首先连接的数据库和数据表必须支持事务 mysql 1.InnoDB和MyISAM对事务的支持怎么样? I ...

  2. Android RecyclerView使用 及 滑动时加载图片优化方案

    1.控制线程数量 + 数据分页加载2.重写onScrollStateChanged方法 这个我们后面再谈,下面先来看看RecyclerView控件的使用及我们为什么选择使用它 RecyclerView ...

  3. Android RecyclerView.Adapter notifyDataSetChanged 不起作用

    我在自己动手写RecyclerView的上拉加载更多,最后就差一步,这个时候数据已经加载完了,UI上面没有显示,我而且也调用了notifyDataSetChanged刷新item的数据,但是一直没效果 ...

  4. (转载) Scrollview 嵌套 RecyclerView 及在Android 5.1版本滑动时 惯性消失问题

    Scrollview 嵌套 RecyclerView 及在Android 5.1版本滑动时 惯性消失问题 标签: scrollviewandroid滑动嵌套 2015-07-16 17:24 1112 ...

  5. Android 关于ListView中adapter调用notifyDataSetChanged无效的原因

    话说这个问题已经困扰我很久了,一直找不到原因,我以为只要数据变了,调用adapter的notifyDataSetChanged就会更新列表,最近在做微博帐号管理这一块,想着动态更新列表,数据是变了,但 ...

  6. Android开发之关于ListView中adapter调用notifyDataSetChanged无效的原因

    1.数据源没有更新,调用notifyDataSetChanged无效. 2.数据源更新了,但是它指向新的引用,调用notifyDataSetChanged无效. 3.数据源更新了,但是adpter没有 ...

  7. Android RecyclerView的基本使用

    Android RecyclerView 在去年的Google I/O大会上就推出来了,以前经常使用的ListView 继承的是AbsListView,而RecyclerView则直接继承 ViewG ...

  8. 极简的Android RecyclerView Adapter(使用DataBinding)

    阅读本篇文章需要读者对Android Databinding和RecyclerView有一定的了解. 简介 我们知道,DataBinding的核心理念是数据驱动.数据驱动驱动的目标就是View,使用D ...

  9. (转载) Android RecyclerView 使用完全解析 体验艺术般的控件

    Android RecyclerView 使用完全解析 体验艺术般的控件 标签: Recyclerviewpager瀑布流 2015-04-16 09:07 721474人阅读 评论(458) 收藏  ...

随机推荐

  1. java虚拟机(三)-- 虚拟机类加载机制

    1.类加载的时机:类从被加载到虚拟机内存中开始,到卸载出内存为止.包含以下几个阶段: 1.加载 2.验证 3.准备 4.解析 5.初始化 6.使用 7.卸载 2.类加载器的种类 1.启动类加载器:这个 ...

  2. jconsole远程连接 jmx配置注意事项

    由于在测试程序时需要收集程序运行时的内存,CPU等消耗情况.选择了jconsole这个jdk自带工具来观察.为了不影响程序运行状态,用远程连接的方式来具体观察. 首先,程序是放在ubutun系统服务器 ...

  3. - > 贪心基础入门讲解四——独木舟问题

    n个人,已知每个人体重,独木舟承重固定,每只独木舟最多坐两个人,可以坐一个人或者两个人.显然要求总重量不超过独木舟承重,假设每个人体重也不超过独木舟承重,问最少需要几只独木舟? 分析: 一个显然的策略 ...

  4. DELPHI7加载UNICODE编码格式的TXT显示为乱码的解决方法

    DELPHI7的STRING默认是ANSI编码,加载UNICODE编码格式的TXT显示为乱码,解决方法如下: procedure TForm1.Button1Click(Sender: TObject ...

  5. 在psql客户端中修改函数

    \ef 创建一个新的函数. \df 显示已经创建的函数. \df+    somefunc 显示这个函数的详细定义 \ef   somefunc 编辑这个函数, 编辑保存退出之后,要执行 \g ,刚才 ...

  6. 一份关于jvm内存调优及原理的学习笔记(转)

    JVM 一.虚拟机的基本结构 1.jvm整体架构 类加载子系统:负责从文件系统或者网络中加载class信息,存入方法区中. 方法区(Perm):存放加载后的class信息,包括静态方法,jdk1.6以 ...

  7. 使用SwipeRefreshLayout和RecyclerView实现仿“简书”下拉刷新和上拉载入很多其它

    一.概述 本篇博客介绍的是怎样使用SwipeRefreshLayout和RecyclerView实现高仿简书Android端的下拉刷新和上拉载入很多其它的效果. 依据效果图能够发现,本案例实现了例如以 ...

  8. 一个性能较好的jvm參数配置以及jvm的简单介绍

    一个性能较好的webserverjvm參数配置: -server //服务器模式 -Xmx2g //JVM最大同意分配的堆内存,按需分配 -Xms2g //JVM初始分配的堆内存.一般和Xmx配置成一 ...

  9. jquery注冊文本框获取焦点清空,失去焦点赋值

    在我们开发过程中特别是用户注冊时会有一个效果.就是文本框获取焦点清空提示,假设用户没有输入信息失去焦点赋值上我们的提示语 <html> <head> <meta http ...

  10. python-pexpect_01安装

    一:python2.7.12安装   #获取python2.7.12 wget https://www.python.org/ftp/python/2.7.12/Python-2.7.12.tgz   ...