关于fragment到底是否可见的问题 以及 什么时候调用才能真正意义上实现可见加载
2016-07-01 23:04
495 查看
今天面试的时候被问到,viewpager+fragment组合使用以及不组合 将Fragemnt与activity 直接使用,组合使用的预加载问题如何处理,不组合使用fragment又有什么方法来判断是否可见,我之前碰到过的就是组合使用在 setUserVisibleHint方法中判断所以就阐述了一种 后来被告知如果不使用viewpager setUserVisibleHint是不会执行的,会执行另外一个onHiddenChanged的方法来判断,后回来测试,在补充一点见解了。。。不喜勿喷
首先上述说到的情况要针对不行的情况进行分析
1.在activity中使用
也就是在把Fragment在xml布局中引入,或者说用addFragment or replace方法,这样我们可以根据Fragment的Onresume和onPause方法就可以判断显示隐藏
2。不同的情况来了,当我们使用FragmentManager的show hide方法来显示和隐藏fragment的时候
提升了切换速率,Fragment没有被销毁,虽然不可见 但是不会执行pause方法,这时候可以来监听onhiddenchanged方法,
注意咯重点内容 开始监听着Fragment的生命周期执行变化,手贱碰了home 此时发现Fragment已经在屏幕上不可见了,但是并没有走onHiddenChanged的方法反而走了onpause这时候我们要在onpause中调用hidefragmet方法来触发onHiddenChanged 下面是onHiddenChanged方法
根据上面的描述我们不能完全依赖onHiddenChanged来判断显示隐藏要结合onpause来使用
好了 下面我们说第三种情况
3 ViewPager+Fragment
ViewPager有一个特性 预加载左右两侧的页面,如果有三个的话(A,B,C)当A->B 这时候虽然C 不可见-但是已经在加载了C的OnResume方法被调用了,在这时候我们就要判断是否当前的Fragment是不是展示给用户可见,也就是经常用到的fragment懒加载…..(当Fragment显示的时候在开始加载)
注意咯重点内容 setUserVisibleHint 只有在Fragment切换的时候才被调用,如果我有fragment-A Fragment-B Activity-C 如果我从A->C 这时候setUserVisibleHint不会被调用,只能通过onpause 来判断,在其中加上hide的方法,进入的时候在onresume中加上show的方法来手动触发,所以我们也不能完全依赖setUserVisibleHint也需要结合activity的onresume与onpause来进行处理
下面我提供一个已经实现懒加载的Fragment 如果要实现只要切换fragment就要刷新数据
在子Fragmen中重写onVIsiable 加载数据的方法放到里面就行后面碰到的问题 mRootView 是空 在下面有具体的解决方法
后续 碰到一些列的开发问题
对于上面的BaseFragment 直接调用onVisable 这种情况适用于 当需要可见加载数据的Fragment 不在首个位置 否则 onVisable中加载数据绑定控件 mrootView 是空的 应为 在BaseFragment 中 判断的是setUserVisableHide 此方法是在oncreateview’ 之前执行 所以在刚可见就调用会出现空的问题,ok 好 对于现在这种问题 我在onResume中再次调用了setUserVisableHide ,这样可以达到预期的效果,看到这里 有心的 会想到 后面的Fragment加载数据是不是也会执行两次,这里我回答一下各位,ViewPager 预加载是存在的取消什么的 也行 但是很麻烦
我在这里通过生命周期的变化来实现数据的延迟加载 也就是说你后面的Framgent的o’ncreateView 的方法在预加载的过程里面已经被执行了
所以 从效果上来说除了第一个Fragment 会执行两次 剩下相邻的都是执行一次 下面各位看下代码估计就能理解了
首先上述说到的情况要针对不行的情况进行分析
1.在activity中使用
也就是在把Fragment在xml布局中引入,或者说用addFragment or replace方法,这样我们可以根据Fragment的Onresume和onPause方法就可以判断显示隐藏
2。不同的情况来了,当我们使用FragmentManager的show hide方法来显示和隐藏fragment的时候
提升了切换速率,Fragment没有被销毁,虽然不可见 但是不会执行pause方法,这时候可以来监听onhiddenchanged方法,
注意咯重点内容 开始监听着Fragment的生命周期执行变化,手贱碰了home 此时发现Fragment已经在屏幕上不可见了,但是并没有走onHiddenChanged的方法反而走了onpause这时候我们要在onpause中调用hidefragmet方法来触发onHiddenChanged 下面是onHiddenChanged方法
根据上面的描述我们不能完全依赖onHiddenChanged来判断显示隐藏要结合onpause来使用
@Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); if(hidden){ //TODO now visible to user } else { //TODO now invisible to user } }
好了 下面我们说第三种情况
3 ViewPager+Fragment
ViewPager有一个特性 预加载左右两侧的页面,如果有三个的话(A,B,C)当A->B 这时候虽然C 不可见-但是已经在加载了C的OnResume方法被调用了,在这时候我们就要判断是否当前的Fragment是不是展示给用户可见,也就是经常用到的fragment懒加载…..(当Fragment显示的时候在开始加载)
注意咯重点内容 setUserVisibleHint 只有在Fragment切换的时候才被调用,如果我有fragment-A Fragment-B Activity-C 如果我从A->C 这时候setUserVisibleHint不会被调用,只能通过onpause 来判断,在其中加上hide的方法,进入的时候在onresume中加上show的方法来手动触发,所以我们也不能完全依赖setUserVisibleHint也需要结合activity的onresume与onpause来进行处理
/** * 如果是与ViewPager一起使用,调用的是setUserVisibleHint * * @param isVisibleToUser 是否显示出来了 */ @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (getUserVisibleHint()) { isVisible = true; onVisible(); } else { isVisible = false; onInvisible(); } }
下面我提供一个已经实现懒加载的Fragment 如果要实现只要切换fragment就要刷新数据
在子Fragmen中重写onVIsiable 加载数据的方法放到里面就行后面碰到的问题 mRootView 是空 在下面有具体的解决方法
package com.xmagicj.android.lazyfragment; import android.os.Bundle; import android.support.v4.app.Fragment; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** * <pre> * 若把初始化内容放到initData实现 * 就是采用Lazy方式加载的Fragment * 若不需要Lazy加载则initData方法内留空,初始化内容放到initViews即可 * * 注1: * 如果是与ViewPager一起使用,调用的是setUserVisibleHint。 * * 注2: * 如果是通过FragmentTransaction的show和hide的方法来控制显示,调用的是onHiddenChanged. * 针对初始就show的Fragment 为了触发onHiddenChanged事件 达到lazy效果 需要先hide再show * eg: * transaction.hide(aFragment); * transaction.show(aFragment); * * Created by MnyZhao * on 2015/11/2. * </pre> */ public abstract class BaseFragment extends Fragment { /** * Fragment title */ public String fragmentTitle; /** * 是否可见状态 */ private boolean isVisible; /** * 标志位,View已经初始化完成。 * 2016/04/29 * 用isAdded()属性代替 * 2016/05/03 * isPrepared还是准一些,isAdded有可能出现onCreateView没走完但是isAdded了 */ private boolean isPrepared; /** * 是否第一次加载 */ private boolean isFirstLoad = true; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 若 viewpager 不设置 setOffscreenPageLimit 或设置数量不够 // 销毁的Fragment onCreateView 每次都会执行(但实体类没有从内存销毁) // 导致initData反复执行,所以这里注释掉 // isFirstLoad = true; // 2016/04/29 // 取消 isFirstLoad = true的注释 , 因为上述的initData本身就是应该执行的 // onCreateView执行 证明被移出过FragmentManager initData确实要执行. // 如果这里有数据累加的Bug 请在initViews方法里初始化您的数据 比如 list.clear(); isFirstLoad = true; View view = initViews(inflater, container, savedInstanceState); isPrepared = true; lazyLoad(); return view; } /** * 如果是与ViewPager一起使用,调用的是setUserVisibleHint * * @param isVisibleToUser 是否显示出来了 */ @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (getUserVisibleHint()) { isVisible = true; onVisible(); } else { isVisible = false; onInvisible(); } } /** * 如果是通过FragmentTransaction的show和hide的方法来控制显示,调用的是onHiddenChanged. * 若是初始就show的Fragment 为了触发该事件 需要先hide再show * * @param hidden hidden True if the fragment is now hidden, false if it is not * visible. */ @Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); if (!hidden) { isVisible = true; onVisible(); } else { isVisible = false; onInvisible(); } } /** *可见时调用 */ protected void onVisible() { lazyLoad(); } /** *可见时调用 */ protected void onInvisible() { } /** * 不可见调用 */ protected void lazyLoad() { if (!isPrepared || !isVisible || !isFirstLoad) { //if (!isAdded() || !isVisible || !isFirstLoad) { return; } isFirstLoad = false; initData(); } protected abstract View initViews(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); protected abstract void initData(); public String getTitle() { if (null == fragmentTitle) { setDefaultFragmentTitle(null); } return TextUtils.isEmpty(fragmentTitle) ? "" : fragmentTitle; } public void setTitle(String title) { fragmentTitle = title; } /** * 设置fragment的Title直接调用 {@link BaseFragment#setTitle(String)},若不显示该title 可以不做处理 * * @param title 一般用于显示在TabLayout的标题 */ protected abstract void setDefaultFragmentTitle(String title); }
后续 碰到一些列的开发问题
对于上面的BaseFragment 直接调用onVisable 这种情况适用于 当需要可见加载数据的Fragment 不在首个位置 否则 onVisable中加载数据绑定控件 mrootView 是空的 应为 在BaseFragment 中 判断的是setUserVisableHide 此方法是在oncreateview’ 之前执行 所以在刚可见就调用会出现空的问题,ok 好 对于现在这种问题 我在onResume中再次调用了setUserVisableHide ,这样可以达到预期的效果,看到这里 有心的 会想到 后面的Fragment加载数据是不是也会执行两次,这里我回答一下各位,ViewPager 预加载是存在的取消什么的 也行 但是很麻烦
我在这里通过生命周期的变化来实现数据的延迟加载 也就是说你后面的Framgent的o’ncreateView 的方法在预加载的过程里面已经被执行了
所以 从效果上来说除了第一个Fragment 会执行两次 剩下相邻的都是执行一次 下面各位看下代码估计就能理解了
package com.baisi.boost.cleaner.manager.fragment; import android.os.Bundle; import android.support.v4.app.Fragment; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** * <pre> * 若把初始化内容放到initData实现 * 就是采用Lazy方式加载的Fragment * 若不需要Lazy加载则initData方法内留空,初始化内容放到initViews即可 * * 注1: * 如果是与ViewPager一起使用,调用的是setUserVisibleHint。 * * 注2: * 如果是通过FragmentTransaction的show和hide的方法来控制显示,调用的是onHiddenChanged. * 针对初始就show的Fragment 为了触发onHiddenChanged事件 达到lazy效果 需要先hide再show * eg: * transaction.hide(aFragment); * transaction.show(aFragment); * * Created by MnyZhao * on 2015/11/2. * </pre> */ public abstract class BaseFragment extends Fragment { /** * Fragment title */ public String fragmentTitle; /** * 是否可见状态 */ private boolean isVisible; /** * 标志位,View已经初始化完成。 * 2016/04/29 * 用isAdded()属性代替 * 2016/05/03 * isPrepared还是准一些,isAdded有可能出现onCreateView没走完但是isAdded了 */ private boolean isPrepared; /** * 是否第一次加载 */ private boolean isFirstLoad = true; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 若 viewpager 不设置 setOffscreenPageLimit 或设置数量不够 // 销毁的Fragment onCreateView 每次都会执行(但实体类没有从内存销毁) // 导致initData反复执行,所以这里注释掉 // isFirstLoad = true; // 2016/04/29 // 取消 isFirstLoad = true的注释 , 因为上述的initData本身就是应该执行的 // onCreateView执行 证明被移出过FragmentManager initData确实要执行. // 如果这里有数据累加的Bug 请在initViews方法里初始化您的数据 比如 list.clear(); isFirstLoad = true; View view = initViews(inflater, container, savedInstanceState); isPrepared = true; lazyLoad(); return view; } /** * 如果是与ViewPager一起使用,调用的是setUserVisibleHint * * @param isVisibleToUser 是否显示出来了 */ @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (getUserVisibleHint()) { System.out.println("执行一次>>>>>>>>>>>>>>>>>"); isVisible = true; onVisible(); } else { isVisible = false; onInvisible(); } } /** * 如果是通过FragmentTransaction的show和hide的方法来控制显示,调用的是onHiddenChanged. * 若是初始就show的Fragment 为了触发该事件 需要先hide再show * * @param hidden hidden True if the fragment is now hidden, false if it is not * visible. */ @Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); if (!hidden) { isVisible = true; onVisible(); } else { isVisible = false; onInvisible(); } } /*再次调用可见方法*/ @Override public void onResume() { super.onResume(); if (getUserVisibleHint()) { setUserVisibleHint(true); } } protected void onVisible() { lazyLoad(); } protected void onInvisible() { } /** * 要实现延迟加载Fragment内容,需要在 onCreateView * isPrepared = true; */ protected void lazyLoad() { if (!isPrepared || !isVisible || !isFirstLoad) { //if (!isAdded() || !isVisible || !isFirstLoad) { return; } isFirstLoad = false; initData(); } protected abstract View initViews(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); protected abstract void initData(); public String getTitle() { if (null == fragmentTitle) { setDefaultFragmentTitle(null); } return TextUtils.isEmpty(fragmentTitle) ? "" : fragmentTitle; } public void setTitle(String title) { fragmentTitle = title; } /** * 设置fragment的Title直接调用 {@link BaseFragment#setTitle(String)},若不显示该title 可以不做处理 * * @param title 一般用于显示在TabLayout的标题 */ protected abstract void setDefaultFragmentTitle(String title); }
相关文章推荐
- Python函数讲解
- LOL匹配算法
- 自定义View:点、图片随手指而移动的View
- 详解Android Activity的四种launchMode
- 欢迎使用CSDN-markdown编辑器
- 过滤器Filter
- 手写体识别(基于Opencv)
- STM32boot启动
- 172. Factorial Trailing Zeroes
- 《Windows程序设计》读书笔七 鼠标
- Android java传递int类型数组给C
- 【转载】深入探讨透视投影坐标变换
- HDU5697 刷题计划 dp+最小乘积生成树
- java数据类型
- 做人的原则
- 进击的码农-6
- 自己写的自定义View,M收音机频率view,85MHz-108MHz。
- Qt 多线程与数据库操作需要注意的几点问题(QSqlDatabase对象只能在当前线程里使用)
- 数据结构 树 相关面试试题
- Python案例-开发之路-设计模式-单例模式