【问题标题】:Fragment view in ViewPager is not restored when resumingViewPager 中的 Fragment 视图在恢复时未恢复
【发布时间】:2014-03-22 20:32:47
【问题描述】:

我设置了 ActionBar 选项卡。它由 4 个选项卡组成。一切都很好,直到我离开 TabbedFragment 并返回。

我创建这样的标签:

  @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final ActionBar actionBar = getActionBar();

        tabs = Lists.newArrayList();
        tabs.add(new TabDefinition<>("Tab 1"));
        tabs.add(new TabDefinition<>("Tab 2"));
        tabs.add(new TabDefinition<>("Tab 3"));
        tabs.add(new TabDefinition<>("Tab 4"));


        for (TabDefinition tab : tabs) {
            actionBar.addTab(actionBar.newTab()
                .setText(tab.text)
                .setTag(tab.tag)
                .setTabListener(this));
        }
    }

然后像这样初始化适配器:

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

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    viewPager = (ViewPager) view.findViewById(R.id.pager);

    viewPager.setAdapter(new FragmentStatePagerAdapter(getFragmentManager()) {

        @Override
        public Fragment getItem(int position) {
            return tabs.get(position).fragment;
        }

        @Override
        public int getCount() {
            return tabs.size();
        }
    });

    viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            getActionBar().setSelectedNavigationItem(position);
        }
    });

    viewPager.setCurrentItem(getActionBar().getSelectedNavigationIndex(), true);
}

当返回TabbedFragment 选中的选项卡和旁边的 1 时不会有任何内容。只是空视图。但是如果我选择 current + 2 片段内容被加载。然后返回到第一个片段内容被重新加载。
例如,我有 A、B、C、D 选项卡。在离开 TabbedFragment 之前,我选择了选项卡 A。 返回TabbedFragment 时,我仍在选项卡A,但它是空的。标签 B 也是如此。
但选择选项卡C时,它会创建和加载。返回到选项卡 A 它被重新创建。

这可能是什么问题?

【问题讨论】:

  • 片段中有 View Pager,现在 tabs 有什么用?你能提供实际的结构吗?
  • 这就是全部。你还需要什么?
  • 好的,非常快速的问题。您在带有 viewpager 的活动中的片段内有一个 viewpager 吗?
  • 是的,Activity 有一个带有 ViewPager 的 Fragment,它显示 Fragment。
  • 你是怎么解决的?

标签: android android-fragments android-tabs


【解决方案1】:

过了一会儿又遇到了同样的问题,所以更新了这个问题。 如果你使用FragmentStatePagerAdapter,你应该通过getChildFragmentManager()而不是getFragmentManager()提供FragmentManager。见Issue 55068: ViewPager doesn't refresh child fragments when back navigation via backstack

【讨论】:

  • 这对支持 v4 FragmentManager 有何作用?
  • 优秀的解决方案。基本上,如果您有一个具有片段的活动,并且该片段然后具有视图寻呼机(加载片段),那么在您的 onResume() 覆盖中,您需要在设置之前使用 getChildFragmentManager() 而不是 getFragmentManager() 调用 PagerAdapter该 PagerAdapter 的 ViewPager 适配器。
  • 我在使用FragmentPagerAdapter 时遇到了这个问题,使用getChildFragmentManager() 也解决了这个问题。
  • 谢谢就是这样...沮丧了 5 天。
  • 你节省了我接下来的 5 个小时。
【解决方案2】:

好的,所以当使用FragmentStatePagerAdapter 时,当您导航超过一个片段时,您的片段将被销毁,因为默认情况下offScreenPageLimit 被默认设置为1,就像上面提到的那样。

通常,此类用于具有大量Fragments 的活动,即必须滚动浏览大量视图。如果您的应用程序不需要超过 3-4 个选项卡,我建议您改用 FragmentPagerAdapter,然后将 offScreenPageLimit 指定为 3,因此如果您到达第 4 个选项卡,之前的所有 3 个选项卡仍将在内存中.

这是我在 github 上创建的项目的一些示例代码,说明了如果您不想添加 offScreenPageLimit,如何动态加载片段。

https://github.com/lt-tibs1984/InterfaceDemo/blob/master/src/com/divshark/interfacedemo/InterfaceDemoMain.java

浏览这个类中的所有这些代码,你会看到我是如何动态加载片段的,每次我的ViewPager 被滑过。最明显的是在底部。

您可以下载此代码,并将其用作您想做的事情的测试基础。 尝试在 viewPager 的 onCreate() 方法中添加 setOffScreenPageLimit(2) 并注意不同的行为。要检查行为,请编辑片段 1 中的文本。导航离开并返回,无论是否设置。你会看到,当它被设置时,片段的文本仍然是你改变它的内容,因为片段永远不会重新创建。

如果您有其他问题,请提供其他问题。

祝你好运

更新

private static final String [] fragmentClasses = {"com.example.project.YourFragment1","com.example.project.YourFragment2","com.example.project.YourFragment3"};



 viewPager.setAdapter(new FragmentStatePagerAdapter(getFragmentManager()) {

    @Override
    public Fragment getItem(int position) {

       Fragment fragmentAtPosition = null;

       // $$$$ This is the Important Part $$$$$
       // Check to make sure that your array is not null, size is greater than 0 , current position is greater than equal to 0, and position is less than length
       if((fragmentClasses != null) && (fragmentClasses.length > 0)&&(position >= 0)&& (position < fragmentClasses.length))
        {
       // Instantiate the Fragment at the current position of the Adapter
        fragmentAtPosition = Fragment.instantiate(getBaseContext(), fragmentClasses[position]);
        fragmentAtPosition.setRetainInstance(true);

       }

        return fragmentAtPosition;
    }

    @Override
    public int getCount() {
        return fragmentClasses.length;
    }
});

【讨论】:

  • 嗯,你的示例代码和我的一样。唯一的区别是适配器。如果我在某些情况下使用FragmentPagerAdapter,它甚至不会在第一次加载片段内容。
【解决方案3】:

我认为问题存在于您用作标签的片段中。恢复后它们似乎没有显示任何内容(请参阅Fragment lifecycle)。只有当前选择的 +/-1 选项卡为空的“奇怪”问题是因为您的 ViewPageroffScreenPageLimit 默认为 1。重新创建高于此阈值的所有选项卡。

因此,在您的情况下,增加该值会导致您的所有选项卡在恢复后显示为空。检查您的 Fragment 代码,您使用哪些生命周期方法来扩展布局、设置适配器等,因为这就是造成您麻烦的原因。

【讨论】:

  • 是的,增加offScreenPageLimit 使所有选项卡都为空。我玩了一下,看起来它们在恢复时根本没有添加到ViewPager。我只能看到空的ViewPager - 片段的背景与ViewPager 不同,以便于调试。
  • 并且它不依赖于Fragment 恢复这些片段时没有调用任何片段 lyfecycle 方法。尝试了几种不同的实现...
  • 我不认为增加价值会完成这项工作,因为 offScreenPageLimit 会加载 ViewPager 的 +/- 页数。
  • 是的,我并不是说增加值会解决问题 --- 它只是解释了为什么只有当前的 +/- 1 个选项卡是空的。跨度>
【解决方案4】:

我猜这是因为在加载片段时,android 会加载当前和 current+1,如果您进行调试,您不会看到 onPause 被立即调用下一个片段。

您可以在 TabHost.OnTabChangeListener 的 onTabChanged() 方法中以编程方式重新加载内容。

【讨论】:

  • 我没有使用 TabHost。我正在使用 ActionBar 选项卡。
  • 好的。那你有没有尝试在 onTabSelected() 方法中重新加载?
  • 我还发现问题是加载了两个片段,但当前片段为空。即使现在也找不到解决方案。谢谢
【解决方案5】:

经过大量研究,这对我有用。 我有一个复杂的布局,一个片段中有 3 个选项卡,它会被其他片段切换出来。我意识到ViewpagerAdapter 将保留状态,即使您按下主页按钮。我的问题是来回切换会使子片段 UI 视图元素无效并崩溃。关键是不要将new 排除在您的ViewPagerAdapter 之外。为适配器添加空检查对我有用。另外,请务必根据您的需要分配setOffscreenPageLimit()。另外,据我了解,setRetainInstance(true); 不应该用于具有 UI 的片段,它是为无头片段设计的。

在包含您的选项卡的片段中:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_tab, container, false);
    tabLayout = (TabLayout) view.findViewById(R.id.tablayout);
    viewPager = (ViewPager) view.findViewById(R.id.viewPager);

    //Important!!! Do not fire the existing adapter!!
    if (viewPagerAdapter == null) {
        viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
        viewPagerAdapter.addFragments(new AFragment(), "A");
        viewPagerAdapter.addFragments(new BFragment(), "B");
        viewPagerAdapter.addFragments(new CFragment(), "C");
    }
    //Allocate retention buffers for three tabs, mandatory
    viewPager.setOffscreenPageLimit(3);
    tabLayout.setupWithViewPager(viewPager);
    viewPager.setAdapter(viewPagerAdapter);

    return view;
}

【讨论】:

  • 这样安全吗?我的意思是,它不会带来内存泄漏?
【解决方案6】:

或者更简单地说,当导航回tabbedfragment(假设您使用意图并且片段位于活动中)时使用:

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

这会保留原始活动并将其移动到堆栈顶部而不是重新创建它,因此您永远不需要重新创建 viewPager。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 2021-03-18
    • 1970-01-01
    相关资源
    最近更新 更多