【问题标题】:Navigating back to FragmentPagerAdapter -> fragments are empty导航回 FragmentPagerAdapter -> 片段为空
【发布时间】:2013-07-14 09:53:01
【问题描述】:

我有一个片段(我称之为pagerFragment),它被添加到后台堆栈并且是可见的。它拥有一个viewPager 和一个FragmentPagerAdapterFragmentPagerAdapter 包含(假设)两个片段:A 和 B。

第一次添加片段效果很好。

Fragment A 有一个按钮,一旦点击,就会将 Fragment (C) 添加到 backstack。

问题是这样的:如果我添加那个片段(C),然后点击返回,pagerAdapter 是空的,我看不到里面的任何片段。

如果我使用 hack,并销毁 pagerFragments onDestroyView() 中的子片段(A 和 B),这解决了问题,虽然我不想使用这个 hack。

任何想法可能是什么问题?

【问题讨论】:

标签: android android-fragments android-viewpager fragmentpageradapter back-stack


【解决方案1】:

我遇到了同样的问题。我的解决方案很简单:

在 onCreateView 我有:

// Create the adapter that will return a fragment for each of the three
// primary sections of the app.
mSectionsPagerAdapter = new SectionsPagerAdapter(getActivity()
    .getSupportFragmentManager());

SectionPageAdapter 是这样的:

class SectionsPagerAdapter extends FragmentPagerAdapter {
...
}

将 getSupportFragmentManager 更改为

mSectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager());

它开始工作了! 希望这会有所帮助。

【讨论】:

  • getChildFragmentManager() 仅适用于 17+ android sdk。 :(
  • @SachinShelke 您需要使用支持库。 ;) developer.android.com/reference/android/support/v4/app/…
  • @mike-mitterer,很棒的答案,你太棒了,非常感谢
  • 天哪,我在这个问题上花了 3 个小时。此修复工作完美。我只是失去了几个小时,我再也见不到了。 fml
  • 谁能解释为什么getSupportFragmentManager() 不起作用,但getChildFragmentManager() 起作用? nested 按下后的浏览量如何?这有副作用吗?
【解决方案2】:

听起来您正在使用嵌套片段,因为您的 ViewPager 位于 PagerFragment 中。您是否将 getChildFragmentManager() 传递给 FragmentPagerAdapter 的构造函数?如果不是,你应该。

我认为您不需要 FragmentStatePagerAdapter,但我会试一试,因为它可以处理保存和恢复 Fragment 状态。您的 onDestroyView() hack 起作用的事实让我认为您可能想要一个 FragmentStatePagerAdapter。

这也可能与 FragmentPagerAdapter 添加 Fragments 的方式有关。 FragmentPagerAdapter 不会将 Fragments 添加到 backstack。想象一下,如果您在 ViewPager 中添加了 10 多个页面,并且用户浏览了它们。用户需要回击 11 次才能退出应用。

也可能与这个帖子有关:Nested Fragments and The Back Stack

另外,我不确定您将片段 C 添加到什么。您是否将其添加到与 ViewPager 相同的容器中?

至少你有几个选项可以调查。在这些情况下,我喜欢调试到 Android SDK 源代码,看看是什么导致了这种行为。我建议使用 AOSP source 并添加 frameworks/support 和 frameworks/base 作为您的 SDK 源。这是了解正在发生的事情并避免在一切正常之前进行随机更改的唯一真正方法。

【讨论】:

    【解决方案3】:

    使用getChildFragmentManager() 代替getSupportFragmentManager()。 它会正常工作。

    【讨论】:

      【解决方案4】:

      我刚刚也遇到了我们项目中的问题。根本原因是 FragmentPagerAdapter 的工作方式:

      FragmentPagerAdapter 只是将他当前不需要的 Fragment 从其 View 中分离出来,但不会将其从其 FragmentManager 中删除。当他想再次显示 Fragment 时,他使用从 ViewPager 的视图 id 和适配器 getItemId(position) 调用返回的 id 创建的标签查看 FragmentManager 是否仍然包含 Fragment。如果他找到一个 Fragment,他只需在 FragmentManager 的更新事务中安排将 Fragment 附加到其视图。只有当他没有以这种方式找到片段时,他才会使用适配器 getItem(position) 调用创建一个新片段!

      包含带有 FragmentPagerAdapter 的 ViewPager 的 Fragment 的问题是,当将包含的 Fragment 放入后端堆栈时,FragmentManager 的内容永远不会被清理。如果包含 Fragment 从返回堆栈返回,它会创建一个新视图,但 FragmentManager 仍然包含附加到旧视图的片段,并且现有片段的附加不再起作用。

      摆脱这个问题的最简单方法是避免嵌套片段。 :)

      第二个最简单的方法是在其他帖子中已经提到,将 ChildFragmentManager 用于 FragmentPagerAdapter,因为这个方法在容器片段的生命周期中得到正确更新。

      由于有些项目(如我目前的项目)无法使用这两个选项,因此我在这里发布了一个解决方案,该解决方案可与任意 FragmentManager 一起使用,方法是将子片段的 hashCode 用作该位置的片段的项目 ID .它是以存储适配器内所有位置的所有片段为代价的。

      public class MyPagerAdapter extends FragmentPagerAdapter {
      
      private static int COUNT = ...;
      private final FragmentManager fragmentManager;
      private Fragment[] subFragments = new Fragment[COUNT];
      private FragmentTransaction cleanupTransaction;
      
      public MyPagerAdapter(FragmentManager fragmentManager) {
          super(fragmentManager);
          this.fragmentManager = fragmentManager;
      }
      
      @Override
      public Fragment getItem(int position) {
          return getSubFragmentAtPosition(position);
      }
      
      @Override
      public int getCount() {
          return COUNT;
      }
      
      @Override
      public long getItemId(int position) {
          return getSubFragmentAtPosition(position).hashCode();
      }
      
      //The next three methods are needed to remove fragments no longer used from the fragment manager
      @Override
      public void startUpdate(ViewGroup container) {
          super.startUpdate(container);
          cleanupTransaction = fragmentManager.beginTransaction();
      }
      
      @Override
      public void destroyItem(ViewGroup container, int position, Object object) {
          super.destroyItem(container, position, object);
          cleanupTransaction.remove((Fragment) object);
      }
      
      @Override
      public void finishUpdate(ViewGroup container) {
          super.finishUpdate(container);
          cleanupTransaction.commit();
      }
      
      private Fragment getSubFragmentAtPosition(int position){
          if (subFragments[position] == null){
              subFragments[position] = ...;
          }
          return subFragments[position];
      }
      

      }

      【讨论】:

        【解决方案5】:

        我遇到了同样的问题,只需一次设置两次适配器即可。

        示例代码:

        private fun displayImg(photo1:String, photo2:String){
            val pager:ViewPager = v?.findViewById(R.id.ProductImgPager)!!
            val arr = ArrayList<String>()
            arr.add(photo1)
            arr.add(photo2)
            pager.adapter = AdapterImageView(fm, arr ,arr.size)
            pager.adapter = AdapterImageView(fm, arr ,arr.size)
        
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-22
          • 1970-01-01
          • 1970-01-01
          • 2018-07-12
          • 1970-01-01
          相关资源
          最近更新 更多