2

I just switched an old Android project from Eclipse to Android Studio and in that process I updated from the old support libraries version 19.0.1 to 22.2.0. After this I get some errors in my previously working code. I dowloaded all SDKs in between and checked where the error first came into play, and it seems to be in version 21.1.0 and above. I looked in the release notes for that version, but could not find anything of interest.

My error is that I am getting a NullPointerException here. Note that this method is part of the Activity, not the parent Fragment.

public synchronized void onRecipesFiltered(ArrayList<Recipe> filteredRecipes)
{   
    FilteredRecipesListFragment filteredRecipesListFragment = (FilteredRecipesListFragment)
        getSupportFragmentManager().findFragmentById(R.id.mm_filtered_recipes_list_fragment);

    // filteredRecipesListFragment becomes null
    filteredRecipesListFragment.onRecipesFiltered(filteredRecipes);
}

The Fragment I am getting here is a ListFragment which is part of an XML file which is inflated at the startup of the app. It seems like this ListFragment is added to a FragmentManager inside the parent Fragment during inflation. This is probably why I get a nullpointer while getting the FragmentManager of the Activity. Thus, my question is:

How can I get the ListFragment in the Activity? Is it necessary to keep a reference to the parent Fragment to get the ListFragment through its FragmentManager? The parent Fragment is inflated when returning new MyFragment() in FragmentPagerAdapter#getItem(), nowhere else, hence it is a bit more tricky to get the proper reference in the Activity.

There is one more thing that should be noted: I saw some messages telling me I should updated to JDK 1.7, which I did (OpenJDK 1.7). I don't remember what I did, but under "External Libraries" I see <1.7> (/usr/lib/jvm/java-1.7.0-openjdk-amd64). Not sure if this matters.

Krøllebølle
  • 2,878
  • 6
  • 54
  • 79

2 Answers2

1

I solved the problem by following this answer. It seems like it is not really possible to get the Fragments from the ViewPager in a pretty way, hence they must be kept as references after creation and fetched when required.

I copy the main code of the answer here, just in case. The main idea is to keep the Fragments in the SparseArray to fetch them when needed. They must be inserted and removed after creation/before removal.

public class MyPagerAdapter extends FragmentStatePagerAdapter {
    SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

    public MyPagerAdapter(FragmentManager fm) {
        super(fm);
    }

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

    @Override
    public Fragment getItem(int position) {
        return MyFragment.newInstance(...); 
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
}

After doing this I could rewrite my method to the following, using getChildFragmentManager() as noted by @Skizo:

public synchronized void onRecipesFiltered(ArrayList<Recipe> filteredRecipes)
{
    MyPagerAdapter myPagerAdapter = (MyPagerAdapter) mMyViewPager.getAdapter();
    FilteredRecipesFragment filteredRecipesFragment = (FilteredRecipesFragment)
            myPagerAdapter.getRegisteredFragment(PagerConstants.PAGE_FILTER_RECIPES);

    if (filteredRecipesFragment == null)
    {
        // Fragments that has not yet been instantiated will be returned as null. Cannot do anything about it.
        // Should be fairly unlikely. See:
        // https://stackoverflow.com/a/15261142/1139324
        return;
    }

    FilteredRecipesListFragment listFragment = (FilteredRecipesListFragment)
            filteredRecipesFragment.getChildFragmentManager().findFragmentById(R.id.mm_filtered_recipes_list_fragment);
    listFragment.onRecipesFiltered(filteredRecipes);
}
Community
  • 1
  • 1
Krøllebølle
  • 2,878
  • 6
  • 54
  • 79
0

Try to replace :

FilteredRecipesListFragment filteredRecipesListFragment = (FilteredRecipesListFragment)
    getSupportFragmentManager().findFragmentById(R.id.mm_filtered_recipes_list_fragment);

to :

 FilteredRecipesListFragment filteredRecipesListFragment = (FilteredRecipesListFragment)
    getChildFragmentManager().findFragmentById(R.id.mm_filtered_recipes_list_fragment);

Read more about getChildFragmentManager() on the getChildFragmentManager() documentation

Skizo-ozᴉʞS ツ
  • 19,464
  • 18
  • 81
  • 148
  • This means that I have to keep a reference to the parent `Fragment` somewhere to be able to call `getChildFragmentManager()`. The method in the question is placed in the `Activity` to act as a gateway between the `Activity` and its `Fragments`. I *could* keep a reference to the parent `Fragment`, but this is part of a `FragmentPagerAdapter`, hence it is not so easy to hold a reference because `FragmentPagerAdapter#getItem()` returns `new FilteredRecipesFragment()`. What is the best way to do this? – Krøllebølle Jul 17 '15 at 07:10
  • I'm out of ideas... let me think about that – Skizo-ozᴉʞS ツ Jul 17 '15 at 11:51