ViewPager与片段 - onPause(),onResume()?

56
使用 ViewPager 和片段时,在选项卡之间移动时,我们的onPauseonResume方法不会被调用。有没有办法在片段中找出我们何时可见或隐藏? 很遗憾,我在onResumeonPause中有一些逻辑,比如注册位置服务,由于onPause直到退出整个应用程序才会被调用,因此从一个选项卡切换到另一个选项卡时永远不会停止该逻辑。

1
好的简单解决方案:https://dev59.com/8FsW5IYBdhLWcg3wDzvs#35450575 .. 解释了如何覆盖Fragment中的 public void setUserVisibleHint(boolean isVisibleToUser) 方法,以了解“当前”选项卡视图是否正在显示。 - Gene Bo
10个回答

18

ViewPager带有OnPageChangeListener接口。通过为上一个和当前显示的页面设置一些标志,您可以模拟此行为。


你能提供更多的信息吗? - Darkhogg
1
我想在OnPageChange()回调期间,您可以在选择的新片段上将UserVisiblityHint设置为true,在旧片段上将其设置为false。然后调用它们各自的onResume()和onPause()方法。然后需要在onResume()中检查用户可见性提示,以确保在onPageChange期间进行调用,而不是在片段创建期间进行标准调用。 但是,是否有真正的标志可以恢复文档记录的行为呢? - 3c71
抱歉,伙计,我出城了。很高兴有人帮忙! - Phix
https://dev59.com/oWMm5IYBdhLWcg3wIsQ2 - Yair Kukielka

10

考虑前面的解决方案不够清晰,这是我如何根据Phix的提示解决的:

在OnPageChange()回调函数中:

    Fragment old_fragment = getActiveFragment(old_position);
    Fragment new_fragment = getActiveFragment(new_position);
    old_f.setUserVisibleHint(false);
    old_f.onPause();
    new_f.setUserVisibleHint(true);
    new_f.onResume();

然后在 onResume() 方法中:

    if (getUserVisibleHint())
        /* resume code */

并在 onPause() 中:

    if (!getUserVisibleHint())
        /* pause code */

这里是 getActiveFragment 代码,避免了对其进行过多搜索:

    return fragmentManager.findFragmentByTag("android:switcher:" + viewPagerId + ":" + position);

NB: Fragment有一个isVisible()方法,但它似乎不是非常一致,因此使用UserVisibleHint。

编辑:将UserVisibleHint替换为私有标志。效果更好!


当然这不是一个好的解决方案,但目前没有其他方法来解决这个操作系统的“漏洞”或行为。为什么片段有onResume() onPause(),但首先从未被调用? - 3c71
4
这并不是操作系统的故障。这意味着“Fragment”仍然在内存中。由于其分页性质,“ViewPager”将保留至少2个“Fragments”在内存中。请查看此答案以了解更多细节。 - Emmanuel
因此,我也说了“行为”。不确定您的评论或提供的链接对讨论有何贡献。问题在于需要一种方法来知道片段何时显示/隐藏,我实现了更多或更少这种方式,除了我调用了一些私有的onVisible和onHidden方法。如果您知道更好的解决方案,为什么不编写自己的代码呢?这个问题的唯一两个答案基本上是相同的。 - 3c71
这绝对是一个 bug,如果你看一下 Fragment 参考文档(http://developer.android.com/reference/android/app/Fragment.html)。基本上,onStart() 使片段对用户可见,onResume() 使片段与用户交互,onPause() 片段不再与用户交互,onStop() 片段不再对用户可见。这些只在创建片段时调用,与它们是否可见无关。 - 3c71
2
正确的解决方案是在OnPageChangeListener中执行逻辑。onPause和onResume用于活动进入和退出后台,而不是可见性更改。 - Dave S

8
如果您正在使用Android支持库(版本11),您可以使用getUserVisibleHint或覆盖setUserVisibleHint()来捕获可见和隐藏的更改。请查看此帖子
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // do something when visible.
    }
}

在片段中覆盖此方法。 - Mia

6

ViewPager库的当前版本允许开箱即用地启用所需的行为,只需将FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT作为第二个参数传递给FragmentPagerAdapter即可。

另外考虑使用取代ViewPagerViewPager2ViewPager2默认情况下调用onPause()onResume(),无需配置。

有关详细信息,请参见此答案


4
解决方案1:

在您的ViewPagers适配器中定义一个SparseArray,如下所示。 在此数组中,我们将保存片段的实例。

SparseArray<Fragment> registeredFragments = new SparseArray<>();

覆盖您的适配器的instantiateItem方法。

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment fragment = (Fragment) super.instantiateItem(container, position);
    registeredFragments.put(position, fragment);
    return fragment;
}

同时,您需要覆盖您的ViewPagers的destroyItem方法。
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    registeredFragments.remove(position);
    super.destroyItem(container, position, object);
}

定义一个新的方法来获取你的 ViewPager Fragments 实例。

public Fragment getRegisteredFragment(int position) {
    return registeredFragments.get(position);
} 

现在,您可以使用ViewPager的OnPageChangeListener来实现您的逻辑。以下是如何实现一个ViewPager的onPageChangeListener的示例:
定义一个整数来保存当前位置:
int currentPos;

yourViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                // Empty method
            }

            @Override
            public void onPageSelected(int position) {
                  // This is paused fragment.
                  final Fragment pausedFragment = yourAdapter.getRegisteredFragment(currentPos);

                  // update current position value
                  currentPos = position;

                  // This is resumed fragment
                  final Fragment resumedFragment = yourAdapter.getRegisteredFragment(currentPos);
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                // Empty method
            }
        });

最后,为了恢复实例状态,请更新您当前的位置,以便在用户离开ViewPager时保持更新。

currentPos = yourViewPager.getCurrentItem();

解决方案2:

另一种解决方案是通过Tag查找您的ViewPager Fragments。您可以使用以下代码生成标签:

@NonNull
public static String generateViewPagerFragmentTag(int viewPagerId, int position) {
        final StringBuilder tagBuilder = new StringBuilder();
        tagBuilder.append("android:switcher:");
        tagBuilder.append(viewPagerId);
        tagBuilder.append(":");
        tagBuilder.append(position);
        return tagBuilder.toString();
    }

找到您的片段(Fragment)

final Fragment yourFragment = getSupportFragmentManager().findFragmentByTag(generateViewPagerFragmentTag(R.id.your_viewpager, currentPos));

编辑:

几年前,当我在寻找获取 ViewPager 当前项目的解决方案时,我尝试了在 Fragment 中使用 userVisibleHint,但发现它并不可靠。我不确定现在是否可靠,但我仍然避免使用。


1
更多的回调会引入更多的错误,简单使用ViewPager2版本1.00以上即可。当您滑动时,您可以在onResume中简单地处理事情,因为只有当您滑动到特定的片段时才会调用onResume。 - shubham chouhan

2
您可以在Fragment中使用以下方法。
public void setUserVisibleHint(boolean isVisibleToUser){

}

isVisibleToUser == true 时,该片段被显示。

1
使用最新版本的Viewpager2,它只会在滚动到该片段时调用onResume(),与Vp1不同,后者会在+1下一个片段之前调用它。

1

经过一些研究,我找到了解决这个问题的完美方案。

当你初始化适配器时,要像这样做:

    import static androidx.fragment.app.FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT;

//(i am using androidx but if you have not yet migrated yet you can use appcompat)

    adapter = new FeedTabsPagerAdapter(getSupportFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);

添加这行代码后,您将完美地收到onResume()和onPause()回调。如果我的代码解决了这个问题,请将其标记为正确的解决方案。干杯!

0

savepopulation的答案中的方法提供更明确的定义:

yourPager.setOnPageChangeListener(new GridViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int row, int column, float rowOffset, float columnOffset, int rowOffsetPixels, int columnOffsetPixels) {

        }

        @Override
        public void onPageSelected(int row, int column) {
            // When on page selected if position is not specific for you
            // you can find your fragment or you can switch position.
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接