Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using FragmentPagerAdapter in a Fragment

I have done some refactoring in some Android code where I was using a FragmentPagerAdapter together with a ViewPager in a view directly connected to an Activity. The FragmentPageAdapter was then moved from the Activity to a separat Fragment (which now is child of that activity) as part of the refactoring.

This change did not go well. The cached tabs are for instance disappearing after an orientation change. NullPointerExceptions are thrown all over the place in my Fragment subclasses as findViewById are not able to find it's View-components (looks like the Fragments' View hierarchy are somehow destroyed after a rotation change).

I am using the latest version of Android Support Library.

Any ideas of what went wrong?

EDIT:

My current implementation:

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);
    }

    ...

}

I am also getting this exception while swiping through the tabs in pager adapter.

E/AndroidRuntime(28706): java.lang.NullPointerException
E/AndroidRuntime(28706):    at android.support.v4.app.Fragment.instantiate(Fragment.java:391)
like image 495
bjorncs Avatar asked Mar 04 '13 07:03

bjorncs


2 Answers

Here is how I solved the problem:

I changed the getItem method to construct and return a new instance (thanks to Luksprog):

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

We need to use the FragmentManager returned by getChildFragmentManger() instead of getActivity().getFragmentManager() to create fragments since the ViewPager is residing in a fragment:

TabsPageAdapter adapter = new TabsPageAdapter(getChildFragmentManager(), getActivity());
like image 133
bjorncs Avatar answered Sep 21 '22 21:09

bjorncs


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

Yes, it's bad and probably the source of your problems after the rotation. The ViewPager will use the getItem method to get fragments when it needs them to show in a page. After a rotation the ViewPager will check its state and see how many fragments it previously had and manually restore them without the getItem method. If you hold fragments outside this mechanism you either need to change the adapter so it only uses the fragments from the list(getItem() is not enough) or you update the list of fragments to only reference the fragments created or recreated by the ViewPager. You most likely don't do this and after a rotation you try to access the list fragments assuming they are the one from the ViewPager, action which will throw NullPointerException.

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

If that GridView is in the Fragment's layout, please access it through getView().findViewById....

like image 36
user Avatar answered Sep 20 '22 21:09

user