在一个Fragment中使用FragmentPagerAdapter

7
我在一些Android代码中进行了重构,其中我使用了FragmentPagerAdapter和ViewPager在一个直接连接到Activity的视图中。作为重构的一部分,FragmentPageAdapter从Activity移动到了单独的Fragment(现在是该Activity的子Fragment)。
这个改变并不好。例如,在方向改变后,缓存的标签会消失。我的Fragment子类中到处都抛出NullPointerException,因为findViewById无法找到它的View组件(看起来像是Fragment的View层次结构在旋转改变后被销毁了)。
我正在使用最新版本的Android Support Library。
有什么想法是怎么出错的?
编辑:
我目前的实现:
public class TabsPageAdapter extends FragmentPagerAdapter {

    ...

    @Override
    public Fragment getItem(int index) {
        // tabs = list of Fragment instances created in contructor of pager adapter - bad habbit?
        return tabs.get(index).getTabFragment();
    }

    ...

}


public class StatsFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.stats_view, container, false);

        TabsPageAdapter adapter = new TabsPageAdapter(getActivity().getSupportFragmentManager(), getActivity());
        ViewPager viewPager = (ViewPager) root.findViewById(R.id.tabPager);
        viewPager.setAdapter(adapter);
        viewPager.setOffscreenPageLimit(3);
        TabPageIndicator pageIndicator = (TabPageIndicator) root.findViewById(R.id.tabIndicator);
        pageIndicator.setViewPager(viewPager);  
        return root;
    }
}


public class MedalsTab extends Fragment {

    private MedalAdapter adapter;

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

    @Override
    public void onActivityCreated(Bundle savedInstanceState){
        super.onActivityCreated(savedInstanceState);
        this.adapter = new MedalAdapter();
        GridView gridView = (GridView) getActivity().findViewById(R.id.grdMedals);
        // Nullpointerexception is thrown here
        gridView.setAdapter(adapter);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        //first saving my state, so the bundle wont be empty.
        //http://code.google.com/p/android/issues/detail?id=19917
        outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
        super.onSaveInstanceState(outState);
    }

    ...

}

我在使用pager adapter滑动标签时也遇到了这个异常。
E/AndroidRuntime(28706): java.lang.NullPointerException
E/AndroidRuntime(28706):    at android.support.v4.app.Fragment.instantiate(Fragment.java:391)

我已经附上了当前的代码。 - bjorncs
2个回答

12

以下是我解决问题的方法:

我将getItem方法更改为构建并返回新实例(感谢Luksprog):

    @Override
    public Fragment getItem(int index) {
        return tabs.get(index).constructFragment();
    }

我们需要使用由getChildFragmentManager()返回的FragmentManager来创建片段,而不是getActivity().getFragmentManager(),因为ViewPager位于一个片段中:
TabsPageAdapter adapter = new TabsPageAdapter(getChildFragmentManager(), getActivity());

2
你的回答“我们需要使用由getChildFragmentManager()返回的FragmentManager来创建片段,而不是getActivity().getFragmentManager(),因为ViewPager位于一个片段中”,完全解决了我的问题。没有人知道这个。谢谢! - Aykut Celik

4

// tabs = list of Fragment instances created in contructor of pager adapter - bad habbit?

是的,这是个不好的习惯,也可能是旋转后出现问题的源头。在需要显示页面时,ViewPager将使用getItem方法获取片段。旋转后,ViewPager将检查其状态,并手动恢复曾经拥有过的片段,而不使用getItem方法。如果您在此机制之外持有片段,那么您需要更改适配器,使其仅使用列表中的片段(getItem()不够用),或者更新片段列表以仅引用由ViewPager创建或重新创建的片段。您很可能没有做到这一点,在旋转后,您尝试访问片段列表,假设它们来自ViewPager,这将导致抛出NullPointerException异常。

GridView gridView = (GridView) getActivity().findViewById(R.id.grdMedals);

如果那个 GridViewFragment 的布局中,请通过 getView().findViewById... 访问它。


谢谢,这解决了所有的崩溃问题。然而,当从StatsFragment导航出去然后再返回时,ViewPager仍然丢失了缓存的选项卡片段。 - bjorncs
@bjorncs 你应该发布一个新问题,附上新的代码并解释一下 losing it's cached tab fragments 的确切含义。 - user
没事了,将子片段管理器传递给ViewPagerAdapter解决了这个问题。 - bjorncs

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