基本上所有 Android 開發都會接觸到 onCreate()
、onDestory()
、onStart()
、onStop()
等這些生命週期方法,但卻不是所有人都會去關注到 onAttachXXX()
這樣的方法群體,今天,筆者就希望用簡短的文章對此進行一定講解。
Activity 中的 onAttachedToWindow
首先在 Activity
中我們可以重寫 onAttachedToWindow()
和 onDetachedFromWindow()
這一對方法。顧名思義,"Attached" 就是附加的意思,所以我們可以確定 onAttachedToWindow()
就是在 View
附加到 window
上的時候進行回撥,而 onDetachedFromWindow()
就剛好相反。
這一對方法會在我們熟悉的 Activity
生命週期的 onResume()
和 onPause()
中間,但並不是每一次 onResume()
和 onPause()
回撥的時候都會在接下來回撥它們。應該比較好理解,我們當然不需要頻繁往 window
中附加和分離 View
嘛。
這裡自然我們容易產生一個問題,在 onAttachedToWindow()
回撥的時候,我們能拿到 View
的寬高麼?換句話說就是這時候 View
是否已經經過了測量和繪製呢?
我們編寫一個 Demo 進行日誌列印看看。
可以看到,並沒有。我們只有在 onWindowFocusChanged
回撥的時候才能真正的拿到 View
的寬高值。
所以,在 Activity
的 onAttachedToWindow()
回撥之後,佈局中的 View
會回撥 onAttachedToWindow()
,然後才會去進行測量和繪製等。那麼我們要獲取一個 View
的寬高就最好是 View.post()
了。
View 的 onAttachedToWindow 使用場景
View
的 onAttachedToWindow()
的呼叫時機會發生在 onMeasure()
之前,那麼它們到底有什麼使用場景呢?
我們在自定義 View 的時候,某些比較重量級的資源,而且不能與其他 View
通用的時候,就可以重寫這兩個方法,並在 onAttachedToWindow()
中進行初始化,onDetachedFromWindow()
方法裡釋放掉。
比如 Bitmap
,雖說現在不用主動呼叫 recycle()
方法來回收,但在 8.0 及以上系統,手動呼叫是會立即釋放所佔用的記憶體的,所以個人認為還是有必要手動回收的,當然了,如果圖片比較小,對記憶體沒什麼影響的就不用了。
再比如一些用作計算的子執行緒,或其他跟 View
顯示有關的任務,在 onDetachedFromWindow()
中也可以停掉了,因為大多數情況下,這些實時資料對於被分離後 View
已經沒有意義了。
RecyclerView.Adapter 的 onViewAttachedToWindow
細心的小夥伴會發現,在 RecyclerView.Adapter
中也會有這麼一個 onViewAttachedToWindow()
和 onViewAttachedToWindow()
。
這兩個方法在列表佈局的時候,用作曝光埋點非常好用,當 Adapter
建立的 View
被視窗分離(即滑動離開了當前視窗介面的)的時候,onViewAttachedToWindow()
會被直接回撥,反之,在列表項 View
在被滑動進螢幕的時候,onViewAttachedToWindow()
會立馬被呼叫。
有了這樣的屬性,對於我們的曝光埋點,就手到擒來了,直接在裡面做就完事兒了。
RecyclerView.Adapter 咋還有一個 onAttachedToRecyclerView
說到 Adapter
的 onViewAttachedToWindow
,咋發現這裡面竟然還有一個 onAttachedToRecyclerView
方法,根據原始碼我們可以發現,onAttachedToRecyclerView()
是在 setAdapter()
的時候觸發。
對比一下,我們便能得出以下它們的使用場景:
RecyclerView
本質上也是一個ViewGroup
,那麼它的Item
要顯示出來,自然需要addView()
進來,移出的時候,當然也要removeView()
出去,所以對應的自然是onViewAttachedToWindow()
和onViewAttachedToWindow()
了。所以在一定場景下,可以通過這兩個回撥來處理一些 Item 移出螢幕,移進螢幕鎖需要的工作。為什麼說一定場景下呢,因為如果呼叫了notifyDataSetChanged()
方法的話,會觸發當前在螢幕中的所有 Item 的onViewAttachedToWindow()
。- 而
onAttachedToRecyclerView
和onAttachedToRecyclerView()
的話,就更加適合做一些資源回收的工作啦。
我的 RecyclerView.Adapter 的 onViewAttachedToWindow 為啥沒起作用?
可能會有小夥伴會遇到這個問題,在遇到這個問題前,先檢查一下你這個 RecyclerView
是否是一個正常滾動的 View
,你如果是被別人巢狀滾動,把自己設定了 isNestedScrollingEnabled
為 false 的話,那你都失去了 Recyclerview
的功用了,那自然是不行的。
可能又有小夥伴說了,由於需求歷史原因,我就是用了 NestedScrollView
巢狀了 Recyclerview
,並禁掉了 Recyclerview
的滑動功能,但又想做上面的曝光埋點功能,那如何是好?
如果是這樣的話,大概你就只能通過類似 View
的 getGlobalVisibleRect()
這樣的方法來判斷 View
的可見性來處理了。關於 View
的可見性分析,這裡就點到為止,大家就自行 Google 吧。