您的位置:首页 > 其它

关于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来使用

@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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: