RecyclerView遇到notifyDataSetChanged無效時的解決方案

640?wx_fmt=png

今日科技快訊

近日有消息稱:蘋果原定於今年秋季和iPhone 7s、7s Plus一同發佈的十週年紀念版本(被猜測爲:iPhone 8、iPhone X、iPhone 7c等),因爲遭遇了重要零部件短缺以及技術挑戰,可能要延期到明年年初才能和消費者見面。另外,由於三星和國產品牌在全球市場的不斷夾擊,蘋果的這次跳票可能會蒙受到極大的損失。

作者簡介

本篇來自 CSDN_LQR 的投稿,分析了Fragment中使用RecyclerView時notifyDataSetChanged可能出現的問題,希望能夠幫助到大家。

CSDN_LQR 的博客地址:

http://www.jianshu.com/u/f9de259236a3

簡述

不管 AbsListView(ListView、GridView) 或是 新出的RecyclerView,在使用 notifyDataSetChanged方法 更新列表數據時,一定要保證數據爲同個對象(即hashCode要一致)。對於這個問題的論證,可以去看官方源代碼,或是看我之前寫的一篇博文

解決ListViews適配器notifyDataSetChanged()無效問題

http://blog.csdn.net/csdn_lqr/article/details/50947825

相信可以幫到你。但是,這個不是本文的重點,本文重點講解在 Fragment 中,RecyclerView 遇到 notifyDataSetChanged 無效的問題。如果你趕時間,可以直接看第三部分("總結")。

探索

1、查看數據(mData)是否是同個對象

tip:java中可以通過打印 hashCode 的方式判斷 mData 是否爲同個對象。

注意:initData方法 在 onActivityCreated() 中被調用。

640?wx_fmt=png

2、操作與結果

tip:常規對 Fragment 的使用,會對其進行緩存,也可能使用單例模式,反正就是短時間內不會重新創建。

①操作一

打開 Activity 後,切換 Fragment(第一次初始化 Fragment)。顯示效果如下:

640?wx_fmt=png

②操作二

切換別的 Fragment 後,再切回剛纔的 Fragment(此前該 Fragment 已經在存在,所以不會再次創建)。顯示效果如下:

640?wx_fmt=png

③看控制檯

640?wx_fmt=png

可以看到數據對象地址一樣,即爲同一個。

3、查看RecyclerView是否是同個對象

說實話,這個是踩坑經驗豐富的網友在羣裏說的,如果不是他說出來,打死我也沒想到,居然還有這麼一個坑。從上面的結果可以看出,adapter 中是有數據的沒錯,而且數據地址沒變,所以理應 notifyDataSetChanged()方法 會生效。但是爲什麼會這樣呢,這裏先賣個關子,先看下面的操作。

①改下上面的代碼,打印RecyclerView的地址

代碼如下:

640?wx_fmt=png

②同上述操作一致

對同一個 Fragment 來回切換,看控制檯輸出。

640?wx_fmt=png

果然不一樣!!!

總結

爲什麼在 Fragment 中 RecyclerView 的地址會發生變化呢?我們先理清一下 Fragment 生命週期會陸續調用的幾個方法:

onCreate() -> onCreateView() -> onActivityCreated() -> onDestroy()

中間少了幾個方法,請不用在意,下面貼下我的 BaseFragment 代碼:

640?wx_fmt=png

當一個 Fragment 在來回切換時,分別調用的方法如下:

第一次顯示:

onCreate() -> onCreateView() -> onActivityCreated()

第二次顯示:

onCreateView() -> onActivityCreated()

這裏不難理解,因爲 Fragment 一般使用的時候會被緩存,所以,當第二次顯示的時候,不會調用 onCreate()。只會調用 onCreateView() 和 onActivityCreated(),這也就是 RecyclerView 地址不一樣的原因所在,因爲控件獲取操作是在 initView() 中進行的,即 RecyclerView 的獲取操作在 onCreateView() 中,而 Fragment 的每次顯示都會調用 onCreateView(),所以 RecyclerView 控件會被再次獲取,即重新創建一個對象(此時hashCode就變化了)。

結論

所以,在 Fragment 中使用 RecyclerView 或 AbsListView 控件的 notifyDataSetChanged()方法 時,除了保證數據(mData對象)不能變以外,控件本身一樣也不能變。

解決方案

因爲 Fragment 的 onCreateView() 和 onActivityCreated() 方法在每次 Fragment 顯示的時候會被調用,控件會被重新創建一次,所以,解決方法只能是在這兩個方法中重新對RecyclerView 設置適配器,而不要使用 notifyDataSetChanged(),故代碼改爲如下:

640?wx_fmt=png

注:只是建議不要在上述兩個生命週期方法中使用 notifyDataSetChanged() 而已,只要在保證 RecyclerView 等列表控件設置完適配器後,可以在任意地方繼續使用 notifyDataSetChanged()。

更多

每天學習累了,看些搞笑的段子放鬆一下吧。關注最具娛樂精神的公衆號,每天都有好心情。

640?wx_fmt=gif

如果你有好的技術文章想和大家分享,歡迎向我的公衆號投稿,投稿具體細節請在公衆號主頁點擊「投稿」菜單查看。

歡迎長按下圖 -> 識別圖中二維碼或者掃一掃關注我的公衆號:

640?wx_fmt=jpeg