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

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

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

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

如何做到这一点?


当前回答

New

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

旧答案(已弃用)

要检测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();
    }
}

解释你可以仔细检查下面的日志,然后我想你可能知道为什么这个解决方案会奏效

首次发布

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项目

希望能有所帮助

其他回答

这似乎恢复了正常的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
}

为此重写Fragment.onHiddenChanged()。

公共void onHiddenChanged(布尔隐藏)当片段的隐藏状态(由isHidden()返回)更改时调用。碎片开始时并不隐藏;每当片段从中改变状态时,都会调用此函数。参数hidden-boolean:如果片段现在隐藏,则为True,如果不可见,则为false。

更新:Android支持库(rev 11)最终修复了用户可见提示问题,现在如果您将支持库用于片段,那么您可以安全地使用getUserVisibleHint()或重写setUserVisibleHint()来捕获gorn的答案所描述的更改。

UPDATE 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支持库r11之前的版本中使用FragmentStatePagerAdapter,则预缓存的片段也会处于“可见”状态(实际上不可见)。

我覆盖:

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

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

我也有同样的问题。ViewPager执行其他片段生命周期事件,我无法更改该行为。我使用片段和可用的动画编写了一个简单的寻呼机。简单寻呼机

在版本androidx.fragment:frage: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)

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