37

My Android app consists three fragments: A, B and C. They're loaded in the two containers defined in the MainActivity layout.

When the app is started, it shows the fragmentA loaded in the left_container and the fragmentC in the right_container.

If you press the button in the fragmentA, a FragmentTransaction changes FragmentC by FragmentB.

At the moment everything OK. But the trouble appears when I try to get a reference to the loaded fragmentB using findFragmentByTag(), because it returns null. I've used the method replace in the FragmentTransaction and I've finished it with commit(), but there isn't way to call FragmentB method. My code:

MainActivity.java:

 public class MainActivity extends Activity{
 static String fragmentTag = "FRAGMENTB_TAG";

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    //Adds the left container's fragment
    getFragmentManager().beginTransaction().add(R.id.left_container, new FragmentA()).commit(); //Adds the fragment A to the left container

    //Adds the right container's fragment
    getFragmentManager().beginTransaction().add(R.id.right_container, new FragmentC()).commit(); //Adds the Fragment C to the right container
 }

 /**
 * Called when the button "Activate Fragment B" is pressed
 */
public void buttonListener(View v){

        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.right_container, new FragmentB(),fragmentTag); //Replaces the Fragment C previously in the right_container with a new Fragment B
        ft.commit(); //Finishes the transaction


        //!!HERE THE APP CRASHES (java.lang.NullPointerException = findFragmentByTag returns null
        ((FragmentB) getFragmentManager().findFragmentByTag(fragmentTag)).testView();


    }
}

FragmentB.java:

public class FragmentB extends Fragment {

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

}


/**
 * Gets a reference to the text_fragment_b TextView and calls its method setText(), changing "It doesn't work" text by "It works!"
 */
public void testView(){
    TextView tv = (TextView)getView().findViewById(R.id.text_fragment_b);
    tv.setText("It works!");
}

}

activity_main.xml:

<?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="horizontal" >

<FrameLayout android:id="@+id/left_container" android:layout_width="0px" android:layout_weight="50" android:layout_height="match_parent"/>    
<FrameLayout android:id="@+id/right_container" android:layout_width="0px" android:layout_weight="50" android:layout_height="match_parent"/>

</LinearLayout>

fragment_b.xml:

<?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" 
android:layout_margin="5sp">

<TextView 
    android:id="@+id/text_fragment_b"
    android:text="It doesn't works!"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

</LinearLayout>

Please help me! I'm a beginner in Android development!

Jonik
  • 80,077
  • 70
  • 264
  • 372
Guillermo Barreiro
  • 1,365
  • 1
  • 11
  • 13

8 Answers8

70

I've fixed it! I called getSupportFragmentManager().executePendingTransactions() after doing the transaction and it worked! After calling that method I can get the fragment using both findFragmentById() and findFragmentByTag() methods.

Shree Krishna
  • 8,474
  • 6
  • 40
  • 68
Guillermo Barreiro
  • 1,365
  • 1
  • 11
  • 13
  • 2
    I'd rather override onAttachFragment and act when the fragment is ready. – Guillaume May 09 '14 at 09:38
  • 1
    I don't see how this solves the problem, or where to put executePendingTransactions(). – IgorGanapolsky Aug 29 '14 at 20:43
  • 4
    @igor-ganapolsky You have to put `executePendingTransactions()`after calling the `.commit()` method of the FragmentTransaction. This forces the system to execute the transaction if it hadn't been done yet. – Guillermo Barreiro Aug 31 '14 at 11:21
  • 9
    It didn't work for me. Add it after the `commit()` and still have a `null` value fragment – Sami Eltamawy May 14 '15 at 10:06
  • If not using v4-compatibility support library, I think you should do `getFragmentManager()`.executePendingTransactions() rather than `getSupportFragmentManager()`.executePendingTransactions(). – ToolmakerSteve Nov 11 '15 at 18:54
  • 1
    Doesn't work for me. Getting "FragmentManager is already executing transactions" exception in some cases when calling executePendingTransactions() and some other fragments get messed up. – Jonas Jan 03 '17 at 11:08
  • 4
    No joy. Still getting null even with `getFragmentManager().executePendingTransactions();` placed after the `.commit()` call. – Hephaestus May 04 '17 at 21:20
  • For those who added executePendingTransactions() and didn't have success, just try adding the Fragment object to your class and reference that in your method that executes the fragment transaction. It's not perfect, but it's a workaround. Make sure you update the Fragment object each time you replace/add a fragment to avoid the object being null... – Shane Sepac Jul 19 '18 at 23:05
  • I didn't understand why this answer is correct)) It doesn;t help for findFragmentByTag. Where is your full answer and real result? – Georgiy Chebotarev Aug 21 '19 at 21:23
3

if you use setRetainInstance(true) than you can't use findFragmentByTag() in onCreate from the Activity. Do it at onResume

see the documentation: setRetainInstance

Ravindra Kushwaha
  • 7,846
  • 14
  • 53
  • 103
oli
  • 569
  • 6
  • 16
1

I'll start by apologising since I'm still very new myself...

I think the problem may be in the declaration of the fragmentTag static String not properly getting access from the class's instances, just change that line to:

private final static String FRAGMENT_TAG = "FRAGMENTB_TAG"; // using uppercase since it's a constant

Also, I would be more explicit when declaring instances, for example:

public void buttonListener(View v){

    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.replace(R.id.right_container, new FragmentB(), FRAGMENT_TAG);
    ft.commit();

    FragmentB fragB = (FragmentB) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
    fragB.testView();
}

I hope you get this sorted, as I seen this question posted earlier and was surprised that it hadn't got any activity yet.

Also, here are a couple of links to the android documentation on replace:

Android Training - Replace

Android Reference - Replace

Kenny164
  • 53
  • 1
  • 8
1

I had the same problem and realized that there is a really simple way to fix this. When using a tag please do make sure to add the

fragmentTransaction.addToBackStack(null); 

method so that your Fragment is resumed instead of destroyed as mentioned in the developer guides.

If you don't call addToBackStack() when you perform a transaction that removes a fragment, then that fragment is destroyed when the transaction is committed and the user cannot navigate back to it. Whereas, if you do call addToBackStack() when removing a fragment, then the fragment is stopped and is later resumed if the user navigates back.

You can find this at the end of this section.

Every time I tried to reference back to my created Fragment, it turns out it had already been destroyed so I lost about 30 minutes trying to figure out why my Fragment was not being found through a simple findFragmentByTag(); call.

Hope this helps!

Panos Gr
  • 667
  • 5
  • 13
0

Be sure you are adding or replacing the fragment in the proper way

Next statement will add the fragment but it will return null when using getFragmentManager().findFragmentByTag(tag):

transaction.add(R.id.mainContent, fragment);


This way it will work;

transaction.add(R.id.mainContent, fragment, tag);
lm2a
  • 835
  • 1
  • 10
  • 19
0

We are also seeing this problem but the cause is slightly different. The suggested solution by https://stackoverflow.com/a/21170693/1035008 doesn't work for us.

void updateFragment(Fragment newFragment) {
    FragmentTransaction ft = getFragmentManager().beginTransaction();

    // We have these 2 lines extra
    Fragment current = getChildFragmentManager().findFragmentByTag(fragmentTag);           
    if (current != null) { ft.remove(current); }

    ft.replace(R.id.right_container, newFragment, fragmentTag); //Replaces the Fragment C previously in the right_container with a new Fragment B
    ft.commit(); //Finishes the transaction

    //!!HERE THE APP CRASHES (java.lang.NullPointerException = findFragmentByTag returns null
    ((FragmentB) getFragmentManager().findFragmentByTag(fragmentTag)).testView();
}

And after reading the documentation about replace:

Replace an existing fragment that was added to a container. This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.

I realize that the remove call was not necessary since it is done by replace automatically. So after delete ft.remove(current), it works fine.

Community
  • 1
  • 1
Yuchen
  • 30,852
  • 26
  • 164
  • 234
0

In my case I used the code to replace and add to BackStack, but set wrong tag:

val fragment = { SomeFragment.newInstance() }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, WrongAnotherFragment.TAG)

Of course, supportFragmentManager.findFragmentByTag(SomeFragment.TAG) didn't find SomeFragment.

CoolMind
  • 26,736
  • 15
  • 188
  • 224
0

For me probably it was a newbie mistake that I was calling super.onCreate(savedInstanceState); after I was trying to access the Fragment using findFragmentByTag.

I moved super.onCreate(savedInstanceState) up in the order and it started working for me.

Mohit Tater
  • 453
  • 1
  • 6
  • 10