关于fragment到底是否可见的问题 以及 什么时候调用才能真正意义上实现可见加载

今天面试的时候被问到,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);
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值