原創文章,轉載請聯繫做者java
Fragment,這個讓人又愛又恨「碎片」。
使用它可讓項目更加輕便--咱們能夠將功能分割、複用,但其複雜的生命週期和Transaction事務
,在極端操做【某些測試人員有一手絕活,三指甚至六指同時觸屏亂彈】下會出現一些不可預期的錯誤--Fragment
嵌套Fragment
,橫豎屏切換等等。
但不管怎樣,面對解決問題,纔是關鍵。這篇文章就是針對Fragment
監測可見狀態改變,提供一種解決方案。git
首先,要說明一下,這裏的可見性就是對用戶來講看的見。不單單是界面位於頂層那種常規狀況,而是即使界面上還存在一層透明界面或是對話框,那麼依然斷定其對用戶可見,爲visible
。
接下來會分析在特定交互環境下,Fragment內部被觸發的方法。github
Fragment
是不能單獨存在的,它所在的視圖樹中,往下追溯,根部必定是一個Activity
。在源碼中,onResume()
方法的描述頗有意思。bash
/**
* Called when the fragment is visible to the user and actively running.
* This is generally
* tied to {@link Activity#onResume() Activity.onResume} of the containing
* Activity's lifecycle. */ @CallSuper public void onResume() { mCalled = true; } 複製代碼
通常狀況下,對用戶可見時觸發。綁定在依賴的Activity生命週期裏網絡
也就是說,通常這個方法,會在可見而且正在活躍時被調用。但說到底,仍是個「窩裏造」,生命週期徹底依賴於父容器
----也必定依賴於根Activity。
那麼不通常的狀況下呢?
有這麼一個例子,在進入一個Activity
界面時,直接調用了beginTransaction().hide(Fragment)
方法。那麼用戶一開始就不會看到這個界面,但生命週期確實也走到了onResume
。由此可知,可見性的判斷不能只依賴於這一個方法的判斷。app
這個方法在使用beginTransaction().hide(Fragment)
會被調用,並且是在onResume
以前。
先來看看源碼裏的描述。ide
/* @param hidden True if the fragment is now hidden, false otherwise.
*/
public void onHiddenChanged(boolean hidden) {
}
複製代碼
這個方法會回調出來一個參數,true的時候表示隱藏了,false表示可見。在可見性改變時被調用。
這裏要注意一下這個布爾值的定義!測試
ViewPager搭配Fragment,也是常見的交互模式了。此時左右滑動時,這個方法會被觸發。但有一點要說明一下,當ViewPager初始化時,Fragment相應的生命週期裏。setUserVisibleHint
方法是走在Fragment的onCreate
以前的。ui
以上幾個方法,就是常見的交互下,會被觸發的方法了。可見性的監測,主要也依賴於這個方法的相互配合。
這裏還須要說明一下,可見性的監測,監測的是「改變」*。也就是當Fragment被建立出來時,不會觸發監測方法,無論它是可見仍是不可見的狀態。*spa
在BaseFragment內,提供了一個onVisibleToUserChanged(boolean isVisibleToUser)
方法做爲內部回調。參數isVisibleToUser
如字面所示,True表示可見,false不可見。當你須要在界面不可見,取消網絡請求或是釋放一些東西,你就可使用此方案。
代碼實現至關簡單,就是一連串邏輯代碼而已。只是在onResume
方法裏,須要判斷一下是否已經觸發了onHiddenChanged
或是setuserVisibleHint
方法。
代碼很短,不到100行。這裏直接貼出來。不方便的小可愛們,能夠直接去GitHub地址.若是你喜歡的話,不妨點個贊吧。
abstract class BaseFragment : Fragment(){
lateinit var mRootView: View
private var isVisibleToUsers = false
private var isOnCreateView = false
private var isSetUserVisibleHint = false
private var isHiddenChanged = false
private var isFirstResume = false
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
isOnCreateView = true
mRootView = LayoutInflater.from(activity).inflate(getResId(), null, false)
return mRootView
}
abstract fun getResId(): Int
override fun onResume() {
super.onResume()
if (!isHiddenChanged && !isSetUserVisibleHint) {
if (isFirstResume) {
setVisibleToUser(true)
}
}
if (isSetUserVisibleHint || (!isFirstResume && !isHiddenChanged)) {
isVisibleToUsers = true
}
isFirstResume = true
}
override fun onPause() {
super.onPause()
isHiddenChanged = false
isSetUserVisibleHint = false
setVisibleToUser(false)
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
isSetUserVisibleHint = true
setVisibleToUser(isVisibleToUser)
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
isHiddenChanged = true
setVisibleToUser(!hidden)
}
private fun setVisibleToUser(isVisibleToUser: Boolean) {
if (!isOnCreateView) {
return
}
if (isVisibleToUser == isVisibleToUsers) {
return
}
isVisibleToUsers = isVisibleToUser
onVisibleToUserChanged(isVisibleToUsers)
}
protected open fun onVisibleToUserChanged(isVisibleToUser: Boolean) {
}
}
複製代碼
以上