MrShadow
MrShadow

Reputation: 171

SupportFragmentManager's findFragmentById always returns a null

I am trying to get a Fragment reference using the findFragmentById() but it always returns a null object. I have used FragmentPagerAdapter and attached a OnPageChangeListener So that whenever I scroll to a specific Fragment, say FragmentB, a method of FragmentB should be fired.

I have the following code:

mPager.addOnPageChangeListener(new OnPageChangeListener() {
    @Override
    public void onPageSelected(int position) {
        if (position == 1) {
            FragmentB fb = (FragmentB) getSupportFragmentManager().findFragmentById(R.id.fragment_b);

            fb.refreshList(); 
            //fb is always null

            Toast.makeText(MainActivity.this,"Scrolled", Toast.LENGTH_SHORT).show();                                                                         
            //Toast is working fine
        }
    }

    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2){}

    @Override
    public void onPageScrollStateChanged(int arg0){}
});

Upvotes: 2

Views: 1699

Answers (2)

Konstantin Loginov
Konstantin Loginov

Reputation: 16000

Just do this instead:

mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        PagerAdapter pagerAdapter = (PagerAdapter) mPager.getAdapter();
        if (position == 1) {
            FragmentB fragmentB = (FragmentB)pagerAdapter.getItem(position);
            fragmentB.refreshList();
        }
    }

    @Override
    public void onPageSelected(int position) {}

    @Override
    public void onPageScrollStateChanged(int state) {}
});

The problem with findFragmentById is not intended to be used in this way:

Finds a fragment that was identified by the given id either when inflated from XML or as the container ID when added in a transaction. This first searches through fragments that are currently added to the manager's activity; if no such fragment is found, then all fragments currently on the back stack associated with this ID are searched.

You should call it to get which fragment is in your fragment's container (for example, if you're using Navigation Drawer and you want to know which of the fragment is in the viewport), while you're using ViewPager, which hosts multiple fragments.

I hope, it helps.

Upvotes: 4

Mike
Mike

Reputation: 4570

findFragmentById() will always return null if you're not declaring your fragment in your activity's content view(it's .xml file) because you can't set a fragment's ID programmatically!

If you're interested in that here is what your code might look like inside an activity's xml file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <fragment
        android:id="@+id/my_id"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.mypackage.MyFragment"/>
</LinearLayout>

And this way, looking up a fragment in the FragmentManager with findFragmentById() would return you the proper instance of MyFragment.

findFragmentById() is one way of looking up fragments in the FragmentManager but there is also another way through findFragmentByTag().

In your case you most probably set up those fragments in an instance of a PagerAdapter through the following method:

@Override
public Fragment getItem(int position) {}

Now there are a lot of examples and answers encouraging you to use some reflection to get a fragment from the FragmentManager. Android automatically generates a tag for each fragment and you can get this name with the following method:

private static String getFragmentName(int viewId, int id) {
    return "android:switcher:" + viewId + ":" + id;
} 

And then you could build a getter method to get a fragment like this:

public Fragment getFragmentForPosition(int position) { 
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(getFragmentName(mViewPager.getId(), getItemId(position)));
    return fragment; // May be null if the fragment is not found. Handle that case too wherever you call this method.
}

Where viewId is the ID from the xml file of the ViewPager and id can be obtained in the adapter anywhere with getItemId(position). This will work but it is a bit of an ugly solution because of the reflection used here.

This is a really easy but ugly solution which should work if the Android team doesn't change their logic for generating tags :)

Please refer to the this SO response for a list of other possible solutions.

Upvotes: 2

Related Questions