@martin0207
2018-12-11T08:46:42.000000Z
字数 4108
阅读 1007
android
开发过程中,不可避免的会遇到ViewPager + Fragment
组合。由于ViewPager的机制,会将ViewPager容器中当前Fragment的左右两个Fragment进行初始化,生命周期到达onResume
,这时候我们在Fragment写的初始化方法,早已经调用,后面的Fragment没有展示,这时候初始化对用户并不友好。
不仅如此,如果我们还想保持Fragment的存活,使用vp.offscreenPageLimit = mAdapter.count
,每个Fragment的初始化再带有个网络请求……爆炸
Fragment类中,含有setUserVisibleHint(boolean isVisibleToUser)
方法,该方法由系统自动调用.
我们可以通过getUserVisibleHint()
方法获取Fragment当前是否对用户可见
接下来,咱们就围绕该方法对Fragment进行封装。
我们将到onResume
之前的生命周期打印一下:
// 第一次创建
E/LazyInitFragment: InheritZeroFragment setUserVisibleHint false
E/LazyInitFragment: InheritZeroFragment setUserVisibleHint true
E/LazyInitFragment: InheritZeroFragment onAttach
E/LazyInitFragment: InheritZeroFragment onCreate
E/LazyInitFragment: InheritZeroFragment onCreateView
E/LazyInitFragment: InheritZeroFragment onActivityCreated
E/LazyInitFragment: InheritZeroFragment onResume
//划出(隐藏)
E/LazyInitFragment: InheritZeroFragment setUserVisibleHint false
//划入(展示)
E/LazyInitFragment: InheritZeroFragment setUserVisibleHint true
可以看出
setUserVisibleHint
默认值为false setUserVisibleHint
先于生命周期方法调用 setUserVisibleHint
方法中直接调用初始化方法一不小心分析出了这么多东西,条件差不多了,下面开始冻手
我总觉得,独立的文字写再多,都不如配合代码读来的真切。所以,我将想要说的内容,都放在代码的注释当中。
上面的分析可以不看,请将代码注释看的仔细些。
将lazyInit()
以及Fragment在onResume
及之前的生命周期状态值提取出来。
interface ILazyInitFragment {
/**
* fragment的状态
* 延迟初始化一般都在这几种状态下(ATTACH除外)
* 所以只添加了这些状态
*/
companion object {
/**
* fragment依附于Activity(默认状态)
* 一般处于该状态下,不做任何操作
*/
const val ON_ATTACH = 0
const val ON_CREATED = 1
const val ON_CREATED_VIEW = 2
const val ON_ACTIVITY_CREATED = 3
const val ON_RESUME = 4
}
/**
* 延迟初始化
*/
fun lazyInit()
}
之所以费劲提取出来,是因为考虑到,已经成型的项目,替换BaseFragment类很费劲,我希望能够让Fragment只实现一个接口就可以实现这个功能。
当然,后话了。
abstract class LazyInitFragment : Fragment(), ILazyInitFragment {
val TAG = this.javaClass.simpleName
/**
* 测试时控制生命周期的日志展示
*/
val mShowLog = true
/**
* fragment的当前状态
* 用来控制lazyInit的调用
*/
var mCurrentState = ILazyInitFragment.ON_ATTACH
/**
* 调用lazyInit的状态判定
* 即mCurrentState>=mLazyInitState时,调用
*/
fun getLazyInitState() = ILazyInitFragment.ON_ACTIVITY_CREATED
/**
* 是否已经初始化
*/
var mInitialized = false
/**
* 设置fragment是否对用户可见
* Android自带的方法,且会自动调用
*/
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (mShowLog) Timber.e("$TAG setUserVisibleHint $isVisibleToUser")
doLazyInit()
}
override fun onAttach(context: Context?) {
super.onAttach(context)
mCurrentState = ILazyInitFragment.ON_ATTACH
if (mShowLog) Timber.e("$TAG onAttach")
doLazyInit()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mCurrentState = ILazyInitFragment.ON_CREATED
if (mShowLog) Timber.e("$TAG onCreate")
doLazyInit()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mCurrentState = ILazyInitFragment.ON_ACTIVITY_CREATED
if (mShowLog) Timber.e("$TAG onActivityCreated")
doLazyInit()
}
override fun onResume() {
super.onResume()
mCurrentState = ILazyInitFragment.ON_RESUME
if (mShowLog) Timber.e("$TAG onResume")
doLazyInit()
}
private fun doLazyInit() {
/*
若达到我们设置的状态条件,则调用方法:
未初始化 且
用户可见 且
当前状态>= 懒加载触发状态
*/
if (!mInitialized && userVisibleHint && mCurrentState >= getLazyInitState()) {
lazyInit()
mInitialized = true
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
if (mShowLog) Timber.e("$TAG onCreateView")
mCurrentState = ILazyInitFragment.ON_CREATED_VIEW
return inflater.inflate(getLayoutRes(), container, false)
}
/**
* 获取fragment的布局Id
*/
abstract fun getLayoutRes(): Int
override fun onPause() {
super.onPause()
if (mShowLog) Timber.e("$TAG onPause")
}
override fun onStop() {
super.onStop()
if (mShowLog) Timber.e("$TAG onStop")
}
/**
* 这里是个小坑
* ViewPager的规则,默认会销毁当前Fragment及左右两个之外的Fragment视图。
* 即:滑到第3个Fragment时,第1个Fragment会被销毁视图,且只调用到这一步
*/
override fun onDestroyView() {
super.onDestroyView()
mInitialized = false
if (mShowLog) Timber.e("$TAG onDestroyView")
}
override fun onDestroy() {
super.onDestroy()
if (mShowLog) Timber.e("$TAG onDestroy")
}
override fun onDetach() {
super.onDetach()
if (mShowLog) Timber.e("$TAG onDetach")
}
}
setUserVisibleHint
这个方法,之前不知道,所以卡了好长时间。Timber
日志工具,也推荐使用。mShowLog
设为flase。