ViewPager 懒加载 lazyload
abstract class LazyFragment5() : Fragment() {
lateinit var fragmentDelegater: FragmentDelegater
lateinit var root: View
var isViewCreated = false
var isVisibleToUser = false
var isVisibleStateLastTime = false //TODO 4 增加变量 需要在onPause onResume中判断 onDestroyView管不了了
abstract fun i(str: String)
abstract fun initView()
abstract fun getLayoutRes(): Int
abstract fun onFragmentLoadData()
abstract fun onFragmentStopLoadingData()
fun set(fragmentDelegater: FragmentDelegater) {
this.fragmentDelegater = fragmentDelegater
}
// TODO 1.2
fun dispatchUserVisibleHint(state: Boolean) {
if (state && isParentInvisible()) //TODO 5.2 过滤后inner fragments不会加载数据 停止加载了 ××××××× 判断 state 没用 不需要
return
if (state) {
isVisibleStateLastTime=true
onFragmentLoadData()
dispatchChildVisibleState(true) //TODO 5.2 解决
} else {
isVisibleStateLastTime=false
onFragmentStopLoadingData()
dispatchChildVisibleState(false) //TODO 5.2 解决
}
}
//TODO 5.2 解决
private fun dispatchChildVisibleState(state: Boolean) {
val childFragmentManager = childFragmentManager
val fragments = childFragmentManager.fragments
if (fragments!=null){
// fragments.foreach
for ( fragment in fragments){
if (fragment is LazyFragment5 && ! fragment.isHidden && fragment.userVisibleHint && !(fragment as LazyFragment5).isVisibleStateLastTime){ //TODO 要加这个 isVisibleStateLastTime cast 竟然是 多余的
(fragment as LazyFragment5).dispatchUserVisibleHint(state)
}else if(fragment is LazyFragment5 && ! fragment.isHidden && !fragment.userVisibleHint && (fragment as LazyFragment5).isVisibleStateLastTime){// TODO 要加这个 isVisibleStateLastTime
(fragment as LazyFragment5).dispatchUserVisibleHint(state)
}
}
/*for (fragment in fragments) { // 循环遍历 嵌套里面的 子 Fragment 来分发事件操作
if (fragment is LazyFragment5 &&
!fragment.isHidden() &&
fragment.getUserVisibleHint()
) {
(fragment as LazyFragment5).dispatchUserVisibleHint(state)
}
}*/
}
}
// 判断fragment是否可见 TODO 1.1
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
i("setUserVisibleHint isVisibleToUser: $isVisibleToUser")
// isVisibleToUser为true,viewpager中呈现该page,可见,其他不可见
//onCreateView之后,才可以更新UI,if(isViewCreated && isVisibleToUser)
//第一个默认显示的page 在onCreateView之后 会再执行本方法,会执行到下面
//但可以控制其他的缓存的fragment不加载数据,即懒加载数据,
//这是加载数据 的入口,运行处
// if(isVisibleToUser ){
// if (isViewCreated)
// onFragmentLoadData()
//
// }else{
// onFragmentStopLoadingData()
// }
// this.isVisibleToUser = isVisibleToUser // get方法代替声明flag标记 getUserVisibleHint()
/*if (isViewCreated) { //移到 onCreateView中 默认选中的page会加载,已缓存的、执行了ligecycle的就不会执行了 放2个?
if (isVisibleToUser) {
// onFragmentLoadData()
dispatchUserVisibleHint(true)
} else {
// onFragmentStopLoadingData()
dispatchUserVisibleHint(false)
}
}*/
// if (isParentInvisible()) //TODO 5.1 检查放到dispatch里边 ××××× 可以放这 ×××××× 为防止切换activity 在pause resume中还有dispatch 只判断它就可以
// return // TODO 这两行包括本方法 仅限制数据的加载与停止,不限制lifecycle方法包括resume 预加载界面
if (isViewCreated) { // TODO view create后才能更新数据
if (isVisibleToUser && !isVisibleStateLastTime) { // TODO 4 从不可见到可见 onDestroyView里的赋值不需要了
dispatchUserVisibleHint(true)
} else if (isVisibleStateLastTime && !isVisibleToUser){ //TODO 4 直接else不可以, 必须从可见到不可见,否则会多余地执行 停止加载数据
dispatchUserVisibleHint(false)
}
}
}
// TODO 5
fun isParentInvisible():Boolean{
val parentFragment= parentFragment
if (parentFragment is LazyFragment5)
{
val fragment =parentFragment // as LazyFragment5 No cast needed
return !fragment.isVisibleStateLastTime
}
return false
}
//TODO 5
//TODO 5 t2 有个viewpager 里面有4个fragment 。显示到t1时,t2里的 内部fragments会加载数据 停止加载数据
//TODO 5 Fragment2_vp_1: 20 fragment -> onCreateView -> isVisibleToUser: true -> onFragmentLoadData 开始加载数据
// ->onActivityCreated -> Fragment2: 2 fragment -> onStart -> Fragment2_vp_1: 20 fragment - onStart
// -> Fragment2: 2 fragment - onResume -> Fragment2_vp_1: 20 fragment - onResume
// ->Fragment2_vp_1: 20 fragment - setUserVisibleHint isVisibleToUser: false
// -> Fragment2_vp_1: 20 fragment - onFragmentStopLoadingData 将暂停一切操作
// ->Fragment2_vp_1: 20 fragment - setUserVisibleHint isVisibleToUser: true
// ->Fragment2_vp_1: 20 fragment -> onFragmentLoadData 开始加载数据
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
i("onCreateView")
root = inflater.inflate(getLayoutRes(), container, false)
initView()
isViewCreated =
true //FragmentPagerAdapter只销毁fragment视图 FragmentStatePagerAdapter销毁fragment对象,
// 为应对FragmentPagerAdapter,应在onDestroyView 置isViewCreated为false TODO 3 t3 切换到t1 再从t1 切换t3 时 t4会执行停止加载
// TODO 3 因为没有销毁掉已经缓存的 isViewCreated==true,再执行setUserVisibleHint(false)就会执行一次onFragmentStopLoadingData()
// TODO 3 解决 在onDestroyView 置isViewCreated为false
// TODO 3 或者加上一个 isVisibleStateLastTime成员变量 确认从可见到不可见 或 从不可见到可见 才执行数据加载 或 停止加载
// TODO 3 dispatchUserVisibleHint(state: Boolean)中 赋值isVisibleStateLastTime=state 并在setUserVisibleHint 修改如下
/*if (isViewCreated) {
// TODO 3 记录上一次可见的状态: && isVisibleStateUP
if (isVisibleToUser && !isVisibleStateLastTime) {
dispatchUserVisibleHint(true)
} else if (!isVisibleToUser && isVisibleStateLastTime) {
dispatchUserVisibleHint(false)
}
}*/
// if(isViewCreated) TODO 可以不用写,这时一定 为true
/*if (isVisibleToUser && !isVisibleStateLastTime) {
dispatchUserVisibleHint(true)
} else {
dispatchUserVisibleHint(false)
}*/
// TODO userVisibleHint 为false,fragment不可见,可能走lifecycle,默认fragment时 为true 再走lifecycle
// TODO 缓存的fragment 为false 后会走lifecycle 为true 就可见了
if (userVisibleHint) this.setUserVisibleHint(userVisibleHint) //this.dispatch xxx // TODO 2.1 代替下面再写一次代码 去其中判断
// TODO 默认的fragment先hint 为true,再走lifecycle,错过setUserVisibleHint的时机,修正,挽回
// if (isViewCreated) {
// if (isVisibleToUser) {
onFragmentLoadData()
// dispatchUserVisibleHint(true)
// } else {
onFragmentStopLoadingData()
dispatchUserVisibleHint(false) // TODO 第一版 这会产生bug,textview 会显示 退出加载数据
// // 已缓存的fragment,由于hint为false,到onCreateView,,就会执行到这 ,注释掉
// }
// }
return root
}
override fun onDestroyView() {
isViewCreated = false // TODO 3
super.onDestroyView()
i("onDestroyView")
}
override fun onAttach(context: Context) {
super.onAttach(context)
i("onAttach")
}
override fun onPause() {
super.onPause()
i("onPause isHidden: $isHidden")
if (userVisibleHint && isVisibleStateLastTime) { //TODO 4 从可见到不可见
dispatchUserVisibleHint(false)
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
i("onActivityCreated")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
i("onCreate")
}
override fun onStart() {
super.onStart()
i("onStart isHidden: $isHidden")
}
override fun onResume() {
super.onResume()
i("onResume isHidden: $isHidden")
if (userVisibleHint && !isVisibleStateLastTime) { //TODO 4 从不可见到可见
dispatchUserVisibleHint(true)
}
}
override fun onDetach() {
super.onDetach()
i("onDetach")
}
override fun onAttachFragment(childFragment: Fragment) {
super.onAttachFragment(childFragment)
i("onAttachFragment")
}
override fun onStop() {
super.onStop()
i("onStop")
}
override fun onDestroy() {
super.onDestroy()
i("onDestroy")
}
//注意 position 和 index 差1 ,映射menu item Image 、pager item,handler tabIndex ,menu item Image别映射错了
//第二点 tabIndex 与Fragment 共同打印log,放在static里 companion object里 共享由于生命周期方法先于tabIndex赋值语句执行,
// 所以打印的多个fragment的几个生命周期方法的tabIndex全为0,
// 所以将tabIndex在构造函数中传入赋值,init中,可行,不知道先执行init 还是super()
// (先有父类,再有子类,先 调用super() 父类构造方法,这时会执行声明周期方法?
// 不一定,setUserVisibleHint()先于lifecycle ,不过是自带方法,不在viewpager,创建后,也没有关联attach到activity,就不会执行lifecycle了吧)
// 或在viewpager fragment的setUserVisibleHint()方法执行时,赋给tabIndex值 ,
// 使用viewpager时,这个方法 先于 生命周期方法执行,之后再执行一次,执行两次
// setOffscreenPageLimit 为2时,1 2 3 fragment为false,
// 然后1 为true,开始执行各fragment的声明周期,其他的2 3 不为true,也执行lifecycle方法,界面等相关,再然后加载数据getData()加载数据
//切换page时,当前page true 变 false ,目标 和 缓存页面 为false若已执行则不再执行,再然后目标page 为 true
//setUserVisibleHint()为true时,已缓存的fragment可能已执行了相关lifecycle 方法,未缓存的,第一次加载的 则没有执行,
open class MyFragment(position:Int) : LazyFragment5() {
var iv: ImageView? =null // TODO 在 Fragment5中 lateinit property iv has not been initialized
var tv: TextView? =null
var tabIndex: Int? = 0//声明 放在 init前面 否则找不到 Variable cannot be initialized before declaration
//或放在setUserVisibleHint() 也行 但会多次执行
init {
tabIndex=position
}
companion object{
val POSITION = "position"
fun newInstance(position: Int): MyFragment {
// var bundle= Bundle()
val bundle = bundleOf(Pair(POSITION, position))
var fragment = MyFragment(position)
fragment.fragmentDelegater = FragmentDelegater(fragment)
fragment.arguments = bundle
return fragment
}
}
override fun initView() {
if (root != null) {
iv = root.findViewById(R.id.iv_content) as ImageView
tv = root.findViewById(R.id.tv_loading) as TextView
tabIndex = arguments?.getInt(POSITION)
}
}
override fun getLayoutRes(): Int {
return R.layout.fragment_lazy_load
}
override fun i(info: String) {
if (fragmentDelegater != null) { //always true
fragmentDelegater.dumpLifeCycle(
"BAT ${this.javaClass.simpleName} ",
"$tabIndex fragment -> $info"
)
}
}
override fun onFragmentLoadData() {
i("onFragmentLoadData 开始加载数据")
getData()
}
override fun onFragmentStopLoadingData() {
i("onFragmentStopLoadingData 将暂停一切操作")
handler.removeMessages(10)
if (timer !=null){
timer.cancel()
}
tv?.visibility=View.VISIBLE
tv?.text="退出加载数据"
}
override fun onResume() {
super.onResume()
// getData() //TODO 第0版 都加载数据,不做懒加载,不做控制
}
private fun getData() {
timer.start()
}
var timer = object : CountDownTimer(1000, 100) {
override fun onFinish() {
handler.sendEmptyMessage(0)
}
override fun onTick(millisUntilFinished: Long) {
}
}
// companion object {
var handler: Handler = object : Handler(Looper.myLooper()!!) {
override fun handleMessage(msg: Message) {
var drawableId: Int = 0
when (tabIndex) {
1 -> drawableId = R.drawable.a
2 -> drawableId = R.drawable.b
3 -> drawableId = R.drawable.c
4 -> drawableId = R.drawable.d
5 -> drawableId = R.drawable.a
}
// if (iv!=null){
iv?.setImageResource(drawableId)
iv?.visibility = View.VISIBLE
// }
// if (tv !=null){
tv?.visibility = View.GONE
// }
//模拟耗时操作
// try {
// Thread.sleep(1000)
// }catch ( e:InterruptedException){
// e.printStackTrace()
// }
}
}
// }
}