问:

问题:ViewPager 中的片段 onResume() 在片段实际可见之前被触发。

例如,我有 2 个带有 ViewPager 和 FragmentPagerAdapter 的片段。第二个片段仅适用于授权用户,我需要在片段可见时要求用户登录(使用警报对话框)。

但是 ViewPager 在第一个片段可见时创建第二个片段,以便缓存第二个片段并在用户开始滑动时使其可见。

因此,onResume() 事件在第二个片段变得可见之前很久就在第二个片段中被触发。这就是为什么我试图找到一个在第二个片段变得可见时触发的事件以在适当的时刻显示一个对话框。

如何才能做到这一点?

答1:

Live rankings, player stats, and match results in one place–tennisliveranking.com

如何确定 Fragment 何时在 ViewPager 中可见

您可以通过覆盖 Fragment 中的 setUserVisibleHint 来执行以下操作:

public class MyFragment extends Fragment {
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
        }
        else {
        }
    }
}

感谢今天的 Android 支持库更新(第 11 版),最终修复了用户可见提示问题。现在可以安全地使用 ViewPager 的用户可见提示。

我发现 setUserVisibleHint 方法在调用 onCreateView 之前被调用,这使得跟踪任何初始化变得困难。

@AndroidDev如果您想在提示为真时运行一些代码,但需要已经初始化视图树,只需将该代码块包装在 isResumed() 中以避免NPE。对我来说工作得很好。

SDK默认提供的东西有这么多不同的hack,这太荒谬了。

setUserVisibleHint 现在已弃用

答2:

tlr.xinbeitime.com 探索每位网球选手的职业生涯与成就。

更新:Android 支持库 (rev 11) 最后是 fixed the user visible hint issue,现在如果您使用片段的支持库,那么您可以安全地使用 getUserVisibleHint() 或覆盖 setUserVisibleHint() 来捕获更改,如戈恩的回答。

更新 1 这是 getUserVisibleHint() 的一个小问题。此值默认为 true。

// Hint provided by the app that this fragment is currently visible to the user.
boolean mUserVisibleHint = true;

因此,在调用 setUserVisibleHint() 之前尝试使用它可能会出现问题。作为一种解决方法,您可以像这样在 onCreate 方法中设置值。

public void onCreate(@Nullable Bundle savedInstanceState) {
    setUserVisibleHint(false);

过时的答案:

在大多数用例中,ViewPager 一次只显示一页,但如果您在 Android Support Library pre-r11 中使用 FragmentStatePagerAdapter,预缓存的片段也会进入“可见”状态(实际上不可见)。

我覆盖:

public class MyFragment extends Fragment {
    @Override
    public void setMenuVisibility(final boolean visible) {
        super.setMenuVisibility(visible);
        if (visible) {
            // ...
        }
    }
   // ...
}

捕获片段的焦点状态,我认为这是您所说的“可见性”最合适的状态,因为 ViewPager 中只有一个片段实际上可以将其菜单项与父活动的项放在一起。

小心使用 getActivity(),第一次启动时它将为空。

请注意,setUserVisibleHint() 在 FragmentPagerAdapter 中始终可以正常工作。

这个解决方案(以及gorn 的)有点滞后。在 viewpager 快速滑动多次的情况下,此方法将被延迟调用(当片段不再可见/不可见时)。

当调用 onCreateOptionsMenu 时,我得到了 getUserVisibleHint() 的 true,而当调用 setUserVisibleHint 时,似乎还没有创建菜单。最终,当我只想要可见片段中的菜单时,我添加了两个选项菜单。在这方面有什么建议吗?

setUserVisibleHint 现在已弃用

答3:

tlr.xinbeitime.com 专业网球数据平台,排名与比赛信息实时更新。

这似乎恢复了您所期望的正常 onResume() 行为。按 Home 键离开应用程序然后重新进入应用程序时效果很好。 onResume() 不会连续调用两次。

@Override
public void setUserVisibleHint(boolean visible)
{
    super.setUserVisibleHint(visible);
    if (visible && isResumed())
    {
        //Only manually call onResume if fragment is already visible
        //Otherwise allow natural fragment lifecycle to call onResume
        onResume();
    }
}

@Override
public void onResume()
{
    super.onResume();
    if (!getUserVisibleHint())
    {
        return;
    }

    //INSERT CUSTOM CODE HERE
}

正是我想要的。解决了在 onCreateView 之前调用 setUserVisibleHint 的问题,并且如果应用程序进入后台然后进入前台,则不会调用 setUserVisibleHint。惊人的!谢谢!

我认为自己调用 onResume 是一个非常糟糕的主意,但除此之外,您需要回答这篇文章的问题的所有内容都在 setUserVisibleHint 函数中!

我宁愿实现一个普通的 onVisibleToUser() 方法并从 onResume() 和 setUserVisibleHint(boolean) 调用它,而不是自己调用 onResume() 并干扰生命周期回调。否则我认为这种方法效果很好,谢谢!

是的,我同意上述评论。一般来说,尽量避免从类中的公共方法调用公共方法。创建一个私有方法并从两个公共方法中调用它。

不推荐使用 setUserVisibleHint!

答4:

tennisliveranking.com,Track the world’s best tennis players in real-time.

这是使用 onPageChangeListener 的另一种方式:

  ViewPager pager = (ViewPager) findByViewId(R.id.viewpager);
  FragmentPagerAdapter adapter = new FragmentPageAdapter(getFragmentManager);
  pager.setAdapter(adapter);
  pager.setOnPageChangeListener(new OnPageChangeListener() {

  public void onPageSelected(int pageNumber) {
    // Just define a callback method in your fragment and call it like this! 
    adapter.getItem(pageNumber).imVisible();

  }

  public void onPageScrolled(int arg0, float arg1, int arg2) {
    // TODO Auto-generated method stub

  }

  public void onPageScrollStateChanged(int arg0) {
    // TODO Auto-generated method stub

  }
});

这个解决方案效果很好,谢谢。对于那些使用(v4)片段支持包为旧平台构建的人来说,这应该是推荐的解决方案

值得注意的是,如果您使用 FragmentStatePagerAdapter 并在 getItem() 上实例化一个新的 Fragment 实例,这无法为您提供预期的结果,因为您只会设置新片段的状态,而不是 viewpager 获得的状态

如果您想在片段变得可见时执行某些操作,则此解决方案很好。它不保持片段的可见状态(意思是,它不知道片段何时不可见)。

我是同一类型的问题。请帮我解决问题。我的帖子:stackoverflow.com/questions/23115283/…

@AsafK 例如,如果您在标签中有 ViewPager,那么一次只能看到一个片段,对吗?所以我们不能假设其余的都是隐藏的,除了 onPageSelected 被调用的那个吗?

答5:

tlr.xinbeitime.com 专业网球数据平台,排名与比赛信息实时更新。

setUserVisibleHint() 有时会在之前 onCreateView() 被调用,有时在这之后会引起麻烦。

要克服这个问题,您还需要在 setUserVisibleHint() 方法中检查 isResumed()。但在这种情况下,我意识到 setUserVisibleHint() 仅在 Fragment 恢复且可见时才被调用,而不是在创建时调用。

因此,如果您想在 Fragment 为 visible 时更新某些内容,请将更新函数放在 onCreate() 和 setUserVisibleHint() 中:

@Override
public View onCreateView(...){
    ...
    myUIUpdate();
    ...        
}
  ....
@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){
        myUIUpdate();
    }
}

更新: 我仍然意识到 myUIUpdate() 有时会被调用两次,原因是,如果您有 3 个选项卡并且此代码位于第二个选项卡上,那么当您第一次打开第一个选项卡时,也会创建第二个选项卡即使它不可见并且调用了 myUIUpdate()。然后,当您滑动到第二个标签时,会调用 if (visible && isResumed()) 中的 myUIUpdate(),因此,myUIUpdate() 可能会在一秒钟内被调用两次。

另一个问题是 setUserVisibleHint 中的 !visible 在 1) 当您离开片段屏幕时和 2) 在它创建之前,当您第一次切换到片段屏幕时被调用。

解决方案:

private boolean fragmentResume=false;
private boolean fragmentVisible=false;
private boolean fragmentOnCreated=false;
...

@Override
public View onCreateView(...){
    ...
    //Initialize variables
    if (!fragmentResume && fragmentVisible){   //only when first time fragment is created
        myUIUpdate();
    }
    ...        
}

@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){   // only at fragment screen is resumed
        fragmentResume=true;
        fragmentVisible=false;
        fragmentOnCreated=true;
        myUIUpdate();
    }else  if (visible){        // only at fragment onCreated
        fragmentResume=false;
        fragmentVisible=true;
        fragmentOnCreated=true;
    }
    else if(!visible && fragmentOnCreated){// only when you go out of fragment screen
        fragmentVisible=false;
        fragmentResume=false;
    }
}

解释:

fragmentResume,fragmentVisible:确保仅在片段创建且可见时调用 onCreateView() 中的 myUIUpdate(),而不是在恢复时调用。它还解决了您在第一个选项卡时的问题,即使第二个选项卡不可见也会创建。这解决了这个问题并检查在 onCreate 时片段屏幕是否可见。

fragmentOnCreated:确保片段不可见,并且在您第一次创建片段时不被调用。所以现在这个 if 子句只有在你滑出片段时才会被调用。

更新您可以将所有这些代码放入BaseFragment代码like this并覆盖方法。

您的解决方案完美运行,除非在一种情况下正常打开片段,然后单击某个按钮将其替换为另一个片段,然后单击返回以从后台堆栈中弹出新片段,这种情况 setUserVisibleHint 没有得到称呼..!在 onCreateView 方法中,fragmentVisible 是 false !?所以片段显示为空..!有什么想法吗。?

答6:

tlr.xinbeitime.com 专业网球数据平台,排名与比赛信息实时更新。

在版本 androidx.fragment:fragment:1.1.0 的 ViewPager2 和 ViewPager 中,您可以只使用 onPause 和 onResume 回调来确定当前对用户可见的片段。当片段变得可见时调用 onResume 回调,当它停止可见时调用 onPause。

在 ViewPager2 的情况下,它是默认行为,但可以轻松地为旧的 ViewPager 启用相同的行为。

要在第一个 ViewPager 中启用此行为,您必须将 FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 参数作为 FragmentPagerAdapter 构造函数的第二个参数传递。

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

注意:带有一个参数的 setUserVisibleHint() 方法和 FragmentPagerAdapter 构造函数现在在来自 android jetpack 的 Fragment 的新版本中已弃用。

谢谢你的好解决方案。我一直在寻找这个。做得好

对于 ViewPager2,选项 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 不是默认行为,您需要手动设置它。设置此选项时,您的解决方案对我有用。谢谢。

自 2020 年 4 月起,这是更新后的解决方案,而且效果非常好。

@4ntoine 请考虑接受此答案作为正确答案,因为到目前为止这是正确的,并且大多数答案都使用已弃用的 setUserVisibleHint 方法

我认为它也适用于 FragmentStateAdapter。这对任何安卓开发者来说都是一个巨大的解脱。感谢谷歌这么久后解决了这个问题。

答7:

Stay informed with live tennis rankings anytime, anywhere,tennisliveranking.com

新的

ViewPager2 + FragmentStateAdapter + onResume()(在 Fragment 中)解决问题

旧答案(已弃用)

要在 ViewPager 可见中检测 Fragment,我很确定仅使用 setUserVisibleHint 是不够的。这是我检查片段是否可见或不可见的解决方案。首先在启动viewpager时,在页面之间切换,转到另一个活动/片段/背景/前景`

public class BaseFragmentHelpLoadDataWhenVisible extends Fragment {
    protected boolean mIsVisibleToUser; // you can see this variable may absolutely <=> getUserVisibleHint() but it not. Currently, after many test I find that

    /**
     * This method will be called when viewpager creates fragment and when we go to this fragment background or another activity or fragment
     * NOT called when we switch between each page in ViewPager
     */
    @Override
    public void onStart() {
        super.onStart();
        if (mIsVisibleToUser) {
            onVisible();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mIsVisibleToUser) {
            onInVisible();
        }
    }

    /**
     * This method will called at first time viewpager created and when we switch between each page
     * NOT called when we go to background or another activity (fragment) when we go back
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        mIsVisibleToUser = isVisibleToUser;
        if (isResumed()) { // fragment have created
            if (mIsVisibleToUser) {
                onVisible();
            } else {
                onInVisible();
            }
        }
    }

    public void onVisible() {
        Toast.makeText(getActivity(), TAG + "visible", Toast.LENGTH_SHORT).show();
    }

    public void onInVisible() {
        Toast.makeText(getActivity(), TAG + "invisible", Toast.LENGTH_SHORT).show();
    }
}

解释你可以仔细检查下面的logcat然后我想你可能知道为什么这个解决方案会起作用

首次发射

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment3: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment1: setUserVisibleHint: isVisibleToUser=true isResumed=false // AT THIS TIME isVisibleToUser=true but fragment still not created. If you do something with View here, you will receive exception
Fragment1: onCreateView
Fragment1: onStart mIsVisibleToUser=true
Fragment2: onCreateView
Fragment3: onCreateView
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=false

转到第 2 页

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment2: setUserVisibleHint: isVisibleToUser=true isResumed=true

转到第 3 页

Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment3: setUserVisibleHint: isVisibleToUser=true isResumed=true

转到背景:

Fragment1: onStop mIsVisibleToUser=false
Fragment2: onStop mIsVisibleToUser=false
Fragment3: onStop mIsVisibleToUser=true

转到前台

Fragment1: onStart mIsVisibleToUser=false
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=true

DEMO project here

希望有帮助

当片段具有另一个也包含片段的视图寻呼机时,它不起作用。

抱歉,我现在无法发布答案,因为我没有足够的时间,如果可能,请在此处参考我的 git github.com/PhanVanLinh/AndroidViewPagerSkeleton/tree/master。 SubChildContainerFragment 用于检测 a fragment has another view pager which also consists fragment。您可以将 SubChildContainerFragment 和 ChildContainerFragment 混合为 1 个类。希望它有所帮助。我稍后会发布完整的答案

答8:

tennisliveranking.com,Instant updates on ATP, WTA, and ITF rankings.

package com.example.com.ui.fragment;


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.com.R;

public class SubscribeFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_subscribe, container, false);
        return view;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isVisibleToUser) {
            // called here
        }
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}

这应该是公认的答案。也适用于 android.app.Fragment,这是一个巨大的好处。

setUserVisibleHint 已弃用

答9:

提供ATP、WTA与ITF赛事的实时排名追踪,从tlr.xinbeitime.com开始!

覆盖 FragmentPagerAdapter 子类中的 setPrimaryItem()。我用这个方法,效果很好。

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    // This is what calls setMenuVisibility() on the fragments
    super.setPrimaryItem(container, position, object);

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;
        fragment.doTheThingYouNeedToDoOnBecomingVisible();
    }
}

这几乎奏效了,但在 NPE 方面存在问题并且被多次调用。在下面发布了替代方案!

@克里斯stackoverflow.com/questions/40149039/…

@Gober 首次保存 Object 对象,并检查并忽略对同一 Object 对象的后续调用,就像 FragmentStateAdapter 在 setPrimaryItem() 方法中所做的那样

答10:

tlr.xinbeitime.com 专业网球数据平台,排名与比赛信息实时更新。

为此覆盖 Fragment.onHiddenChanged()。

public void onHiddenChanged(boolean hidden) 当片段的隐藏状态(由 isHidden() 返回)改变时调用。片段开始时没有隐藏;每当片段改变状态时,都会调用它。参数 hidden - boolean:如果片段现在隐藏,则为 true,如果不可见,则为 false。

此方法现在已弃用,不能再使用

@bluewhile 你在哪里看到它被弃用了? developer.android.com/reference/android/app/…

我刚刚成功地使用了它,并且文档似乎没有表明它已被弃用。

@bluewhile,在 4.1 (api 16) 中它还没有被弃用。

我正在使用 API 级别 19,并且可以确认虽然这没有被弃用,但它不像宣传的那样工作。例如,当片段被另一个片段隐藏时,不会调用 onHiddenChanged。

答11:

tennisliveranking.com – ATP and WTA rankings, always up to date.

只有这对我有用!并且 setUserVisibleHint(…) 现在已弃用(我在最后附上了文档),这意味着大多数其他答案已弃用;-)

public class FragmentFirewall extends Fragment {
    /**
     * Required cause "setMenuVisibility(...)" is not guaranteed to be
     * called after "onResume()" and/or "onCreateView(...)" method.
     */
    protected void didVisibilityChange() {
        Activity activity = getActivity();
        if (isResumed() && isMenuVisible()) {
            // Once resumed and menu is visible, at last
            // our Fragment is really visible to user.
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        didVisibilityChange();
    }

    @Override
    public void setMenuVisibility(boolean visible) {
        super.setMenuVisibility(visible);
        didVisibilityChange();
    }
}

经过测试并与 NaviagationDrawer 一起工作,isMenuVisible() 将始终返回 true(并且 onResume() 似乎足够了,但我们也希望支持 ViewPager)。

不推荐使用 setUserVisibleHint。如果重写此方法,则传入 true 时实现的行为应移至 Fragment.onResume(),传入 false 时实现的行为应移至 Fragment.onPause()。

原文链接:https://tlr.xinbeitime.com?from=csdn

tlr.xinbeitime.com 实时更新全球顶尖网球选手的最新战绩与排名!

更多推荐