6

在纵向模式下,我ViewPager有 3 个片段 A、B、C,但在横向模式下,它只有 2 个片段 A 和 C。所以我FragmentStatePagerAdapter为每个模式创建 2 个。问题是当屏幕方向改变时,ViewPager恢复并使用旧方向的先前片段。例如,当将方向从纵向更改为横向时,ViewPager现在显示 2 个片段 A、B 而不是 A 和 C。我知道为什么会发生这种情况,但找不到一个好的解决方案。

我当前的解决方法是使用不同的 id ViewPager(例如:纵向布局的 id/viewpager_portrait 和横向布局的 id/viewpager_landscape)以防止重用片段,但这会导致内存泄漏,因为旧片段不会被破坏并且仍保留在内存中.

我尝试了一些解决方法,例如在活动中调用 super.onCreate(null)或删除活动中的onCreate片段,但它们都使我的应用程序崩溃。ViewPageronSaveInstanceState

所以我的问题是如何避免在FragmentStatePagerAdapter方向改变时重复使用一个或多个片段?

任何帮助将不胜感激。预先感谢。

4

4 回答 4

3

问题可能是 Android 提供的 s 的内置PagerAdapter实现Fragment假定项目将保持不变,因此保留并重用对Fragment添加到ViewPager. 由于配置更改或进程被终止,FragmentManager即使在重新创建Activity(和s) 之后,这些引用也会保持不变。Fragment

您需要做的是编写自己的实现,PagerAdapter将自定义标签与每个标签相关联,Fragment并将Fragments 存储在基于标签(而不是基于索引)的格式中。在添加一个抽象方法以提供基于该getItem()方法旁边的索引的标签之后,您可以从现有的一个中派生出一个通用实现。当然,您必须从 中删除Fragment在先前配置中添加的孤立/未使用 s ViewPager(同时最好保持其状态)。

如果您不想自己实现整个解决方案,那么可以使用 CWAC-Pager 库ArrayPagerAdapter中的CWAC-Pager库来提供合理的实现,而无需付出任何努力。初始化后,您可以Fragment根据提供的标签分离相关内容,并根据需要从适配器中删除/添加它。

于 2014-05-31T07:43:17.637 回答
1

getItemPosition()在您的 Adapter 中覆盖并返回POSITION_NONE. 因此,当重新创建 ViewPager 时,它将调用getItemPosition(),并且由于您POSITION_NONE已从此处返回,它将调用getItem(). 您应该从 this 返回新的片段getItem()。参考:http: //developer.android.com/reference/android/support/v4/view/PagerAdapter.html#getItemPosition%28java.lang.Object%29

于 2014-06-05T10:21:35.340 回答
1

当您的方向改变时您可以删除片段 B,为什么要为您的查看器使用两个不同的 id?

您可以像这样在 onCreateView() 或 onResume() 中检索片段(此示例在父片段中工作,但也可在父活动中使用,例如 onResume() ):

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

    // ... initialize components, etc.

    pager.setAdapter(pagerAdapter);

    List<Fragment> children = getChildFragmentManager().getFragments();

    if (children != null) {
        pagerAdapter.restoreFragments(children, orientation);
    }
}

然后在你的适配器里面:

@Override
public void restoreFragments(List<Fragment> fragments, int orientation) {
    List<Fragment> fragmentsToAdd = new ArrayList<Fragment>();
    Collections.fill(fragmentsToAdd, null);
    if (Configuration.ORIENTATION_LANDSCAPE == orientation) {
        for (Fragment f : fragments) {
            if (!(f instanceof FragmentB)) {
                fragmentsToAdd.add(f);
            }
        }
    }
    this.fragmentsInAdapter = Arrays.copyOf(temp.toArray(new Fragment[0]), this.fragments.length); // array of all your fragments in your adapter (always refresh them, when config changes, else you have old references in your array!
    notifyDataSetChanged(); // notify, to remove FragmentB
}

那应该行得通。

顺便提一句。如果您使用的是 Support Library V13,则不能使用 FragmentManager.getFragments(),因此您需要通过 id 或 tag 获取它们。

于 2014-06-06T11:10:47.340 回答
1

在您的活动中覆盖 OnConfigurationChanged()(还将 android:configChanges="orientation" 添加到清单中的活动),这样您可以手动管理方向更改。现在“所有”您要做的就是在 OnOrientaionChanged 中更改适配器(同时跟踪当前位置)。通过这种方式,您可以使用单个布局、单个 ViewPager,并且您不必担心片段不会被回收(好吧,您将有大量工作来弥补这一点)。祝你好运!

于 2014-06-06T11:34:13.633 回答