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

本文详细介绍了在Android开发中,Fragment的懒加载实现方法。包括如何判断Fragment的可见性,使用setUserVisibleHint与onHiddenChanged等方法的具体场景,并提供了懒加载的实现代码。

今天面试的时候被问到,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);
}
<think>嗯,用户的问题是关于Fragment生命周期中,当Fragment可见但不可交互时调用的方法。我需要仔细回忆一下Fragment的生命周期方法,并找出对应的阶段。 首先,Fragment的生命周期和Activity有关联,但有些方法专门属于Fragment。比如onAttach、onCreate、onCreateView、onViewCreated、onStart、onResume、onPause、onStop、onDestroyView、onDestroy、onDetach这些方法。 用户提到的“可见但不可交互”应该是在视图已经创建并显示,但还未处于活跃状态的时候。根据引用中的信息,当Fragment被hide或show时,视图的可见性变化不会触发生命周期,而是直接改变可见性。但生命周期还是跟随Activity。 那当Fragment可见但不可交互的时候,可能是在onStart之后,onResume之前?或者是在某些特定的状态切换时? 回想一下,当Fragment被添加到Activity但还未处于前台时,比如在ViewPager中的情况,可能处于onStart状态。这时候Fragment的视图已经可见,但可能还不能与用户交互,因为Activity可能还未进入onResume状态。或者当Activity处于onPause时,Fragment也会进入onPause,此时可能可见但不可交互。 不过根据Android官方文档,当Fragment的视图被创建并添加到视图树中后,会调用onStart,这时候Fragment可见,但可能Activity还未完全进入前台。而onResume则是在Activity的onResume之后调用,此时Fragment处于活跃状态,可以交互。因此,在onStart之后到onResume之前,Fragment可见但不可交互的。或者当Activity进入onPause时,Fragment也会调用onPause,此时可能仍然可见,但不可交互。 但用户的问题可能是指Fragment变为可见但还没准备好交互的情况,这时候应该是onStart方法。比如当Fragment被添加到后台栈,然后返回时,会经历onCreateView到onStart,此时可见但可能还未恢复交互。或者当Fragment被显示但Activity还没完全恢复时。 不过根据引用中的说明,hide和show不会改变生命周期,所以这种情况下不会触发onStop或onStart。因此,可能用户的问题更多是在常规生命周期中,当Fragment可见但不可交互的阶段,比如onStart到onResume之间,或者onPause之后。 这时候可能需要确认各个方法的作用:onStart表示Fragment可见,但此时可能Activity还未处于前台,所以Fragment不可交互;而onResume在Activity的onResume之后调用,此时Fragment可以交互。同样,onPause在Activity的onPause时调用,此时Fragment可能仍然可见,但不可交互。 所以,正确的答案应该是onStart和onPause这两个方法分别对应Fragment变得可见但不可交互的情况。当Fragment进入onStart时,开始可见但可能不可交互,直到onResume才能交互。当Activity进入onPause时,Fragment的onPause被调用,此时可能仍然可见,但不可交互。 但用户的问题可能更具体,比如在Fragment可见但不可交互时调用的方法,应该是onStart和onPause。但需要更准确的信息。 根据Android官方生命周期图,当Fragment变为可见时,会调用onStart,此时可能处于可见但不可交互的状态,直到onResume才可交互。而当Activity进入onPause时,Fragment的onPause被调用,此时可能仍然可见,但不可交互。因此,这两个方法可能对应不同的阶段。用户的问题可能是在这两个阶段中的某个方法。 但问题中的“可见但不可交互”具体是指哪个阶段呢?比如,当Fragment第一次被添加时,会经历onAttach到onResume,其中onStart之后可见,但可能此时还不能交互,直到onResume。或者当另一个Activity覆盖在当前Activity上时,原Activity的Fragment会进入onPause,此时可能仍然可见(比如对话框式的Activity),但不可交互。 所以,正确的回答应该是:当Fragment变得可见但不可交互时,会调用onStart(当从不可见可见时)和onPause(当从可交互变为不可交互时)。 但用户的问题可能指的是某个特定的方法,比如在onStart时是否处于不可交互的状态?或者在onResume之后才能交互? 根据Android文档,onResume是在Fragment可交互之前调用的,所以可能onStart之后可见,但onResume之后才可交互。因此,在onStart到onResume之间,Fragment可见但不可交互。但实际中,可能这个时间非常短暂,因为Activity的onResume会紧接着调用Fragment的onResume。 另一种情况是,当Activity处于onPause状态时,Fragment的onPause被调用,此时Fragment可见但不可交互。例如,当另一个Activity部分覆盖当前Activity,当前Activity的Fragment仍然可见,但无法交互,此时处于onPause状态。 因此,用户的问题的答案应该是:当Fragment可见但不可交互时,调用的方法是onStart(当变为可见时)和onPause(当变为不可交互时)。 但需要更准确的信息,可能用户的问题是指Fragment从不可见可见但尚未可交互的时候,这时候调用的是onStart。或者当Fragment处于可见但被暂停的时候,调用的是onPause。 根据引用中的内容,Fragment的show和hide不会改变生命周期,所以当hide时视图不可见,但生命周期不变。因此,当Fragment可见但不可交互的情况可能发生在onStart和onPause阶段。 最终答案应该是,在Fragment生命周期中,当可见但不可交互时调用的方法是onStart(当变为可见时)和onPause(当变为不可交互时)。不过需要确认官方文档的正确描述。 根据Android官方文档: - onStart(): Fragment可见,但不可交互,直到onResume()。 - onResume(): Fragment开始交互。 - onPause(): Fragment不再交互,但可能仍然可见。 - onStop(): Fragment不再可见。 所以,正确的方法应该是onStart()和onPause()。当Fragment变得可见但不可交互时,调用的是onStart(),当失去交互能力但仍可见时,调用的是onPause()。 因此,用户的问题的答案是:Fragment可见但不可交互时调用的方法为onStart()(进入可见状态)和onPause()(离开可交互状态)。但用户可能需要具体的某个方法,比如当Fragment变为可见但不可交互时调用的方法是onStart(),而当其变为不可交互但仍可见时是onPause()。 所以,用户的问题的答案应该是onStart()和onPause()这两个方法对应不同的阶段。但需要明确用户的问题场景。 用户的问题可能是指当Fragment从不可见变为可见但还没准备好交互时调用的方法,这时候是onStart()。或者当Fragment处于可见但被暂停(比如另一个Activity覆盖)时调用onPause()。 因此,回答中需要说明这两个方法分别对应的阶段。</think>在Fragment生命周期中,以下方法涉及可见性与交互状态的变化: 1. **`onStart()`** - 当Fragment可见但**尚未可交互**时调用。此时视图已加载到界面中,但Fragment尚未处于前台活跃状态[^2]。 2. **`onPause()`** - 当Fragment**失去交互能力但仍可见**时调用。例如Activity被部分遮挡(如弹出对话框),此时Fragment视图仍然可见,但无法响应用户操作[^1]。 --- ### 具体说明 - **从不可见可见但不可交互**:通过`onStart()`标记可见性,但需等待`onResume()`后才能交互。 - **从可交互到不可交互但仍可见**:通过`onPause()`标记失去焦点,但视图可能仍保留在屏幕上。 --- ### 示例代码 ```kotlin override fun onStart() { super.onStart() // 可见但不可交互,初始化非UI相关操作 } override fun onPause() { super.onPause() // 失去交互能力,但可能仍可见,需暂停动画或释放资源 } ``` ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值