原创文章,转载请联系做者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) {
}
}
复制代码
以上