0

I am having some trouble figuring out the proper way to communicate between fragments when using a FragmentPagerAdapter.

For some context I have an Activity that uses the FragmentPagerAdapter to host two fragments. Fragment A has a button and when pressed it changes text in Fragment B.

Everything works as expected except when I press the button after an orientation change, I get a null pointer exception on the EditText I am changing in Fragment B. After days of debugging it looks like when I call the method in Fragment B that changes the text that it is maybe an old instance of the Fragment.

Here's the code:

The FragmentPagerAdapter which is an inner class in my Activity:

public class MyPagerAdapter extends FragmentPagerAdapter {

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

    @Override
    public Fragment getItem(int i) {
        switch (i) {
            case 0:
                return fragmentA;
            case 1:
                return fragmentB;
        }
        return null;
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        return tabs[position];
    }
}

The Fragments get instantiated in the Activities onCreate method as follows:

FragmentA fragmentA; //Declared outside of onCreate
FragmentB fragmentB; //Declared outside of onCreate

fragmentA = FragmentA.newInstance();
fragmentB = FragmentB.newInstance();

In the Fragment that I press the button I set up an interface as follows:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        testTextInterface = (TestTextInterface) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement testTextInterface");
    }
}

Then when I press the button I have an OnClickListener that fires the following code:

testTextInterface.onTextChanged("Some test text");

Which in turn calls the following method in the Activity:

@Override
public void onTextChanged(String testText) {
    fragmentB.updateText(testText); 

}

And then finally that method in FragmentB:

private void updateText(String incomingText){
    myEditText.setText(incomingText);
}

And that's it, this works perfectly well until I rotate the device causing the Activity to be recreated. Then when I press the button the myEditText view is null in FragmentB in the updateText method.

I've been trying to figure out why for 2 days so I was wondering if anyone can spot where I am going wrong or point me in the right direction as to where I might mind out what is causing the issue?

Donal Rafferty
  • 19,707
  • 39
  • 114
  • 191
  • How are you attaching the fragments to the activity? In the oncreate? If so, when you rotate the device onCreate will be called with a bundle that will be not-null. Therefore you should check if `savedInstanceState` is null and only then attach the fragments. Furthermore I would get rid of the onAttach callback and the reference you keep in the fragments to the activity. Instead when you need to call the listener in the activity just do `getActivity()` in the fragment itself. – Joao Sousa Nov 20 '14 at 22:57
  • One more thing, in the adapter, are you keeping instances to fragments as well instead of creating new fragments? If so, check [this answer](http://stackoverflow.com/questions/6976027/reusing-fragments-in-a-fragmentpageradapter). – Joao Sousa Nov 20 '14 at 22:59
  • I don't attach the fragments manually myself, I believe the FragmentPagerAdapter does that automatically in the getItem method? – Donal Rafferty Nov 21 '14 at 01:23

2 Answers2

1

The problem is that when you rotate, Android creates new instances of your fragments for you, but you are not getting references to those new instances. It would be safer to not keep references to the fragments in your activity, but rather use findFragmentByTag when you need to.

@Override
public void onTextChanged(String testText) {
    Fragment fragmentB = getFragmentManager().findFragmentByTag("FragmentB");
    fragmentB.updateText(testText);
}

And, of course, make sure to set the fragment's tag either in XML or in code.

Bruce
  • 2,377
  • 1
  • 17
  • 11
  • The thing is I don't think I can use this with a FragmentPAgerAdapter as the Fragments have no ID? – Donal Rafferty Nov 21 '14 at 01:18
  • 1
    If you only ever have one instance of each type of fragment, then all you need to do is to set an ID or tag right after your call to newInstance. That way you know that every instance of FragmentA or FragmentB will have the tag or ID you've assigned to it. Android will maintain the fragment tag and ID after they re-create the fragments after a rotation. – Bruce Nov 21 '14 at 01:21
1

I agree with Bruce, but if you do need to keep a reference to the fragment in the activity, you can do something like this:

In you activity, implement a method, let's say 'setFragmentInstance'.

In your fragment, override the onActivityCreated method, in which you can either cast the activity to your activity (if the fragment always stay in that activity) and call that setFragmentInstance method, or you can also add a interface in the fragment, implement it activity and calls the interface's method in onActivityCreated to set the fragment instance.

I asked one similar questions before, but soon after that I got clear about it, see DialogFragment members becomes null after screen rotation

Qianqian
  • 2,104
  • 18
  • 28