58

I have a Fragment FR1 that contains several Nested Fragments; FRa, FRb, FRc. These Nested Fragments are changed by pressing Buttons on FR1's layout. Each of the Nested Fragments have several input fields within them; which include things like EditTexts, NumberPickers, and Spinners. When my user goes through and fills in all the values for the Nested Fragments, FR1 (the parent fragment) has a submit button.

How can I then, retrieve my values from my Nested Fragments and bring them into FR1.

  1. All Views are declared and programmatically handled within each Nested Fragment.
  2. The parent Fragment, FR1 handles the transaction of the Nested Fragments.

I hope this question is clear enough and I am not sure if code is necessary to post but if someone feels otherwise I can do so.

EDIT 1:

Here is how I add my Nested Fragments:

tempRangeButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            getChildFragmentManager().beginTransaction()
                    .add(R.id.settings_fragment_tertiary_nest, tempFrag)
                    .commit();

        }
    });

    scheduleButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            getChildFragmentManager().beginTransaction()
                    .add(R.id.settings_fragment_tertiary_nest, scheduleFrag)
                    .commit();
        }
    });

    alertsButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            getChildFragmentManager().beginTransaction()
                    .add(R.id.settings_fragment_tertiary_nest, alertsFrag)
                    .commit();

        }
    });

    submitProfile.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            constructNewProfile();
        }
    });

where my constructNewProfile() method needs the values from my Nested Fragments.

public Fragment tempFrag = fragment_profile_settings_temperature
        .newInstance();
public Fragment scheduleFrag= fragment_profile_settings_schedules
            .newInstance();
public Fragment alertsFrag = fragment_profile_settings_alerts
        .newInstance();

The above refers to the fields of the parent fragment; and how they are initially instantiated.

Rahul
  • 3,293
  • 2
  • 31
  • 43
Timothy Frisch
  • 2,995
  • 2
  • 30
  • 64
  • also have a look at my questions https://stackoverflow.com/questions/70721218/receive-fragment-result-in-navigation-component-fragment and https://stackoverflow.com/questions/70680327/toolbar-icon-handling-using-navigation-component – Taimoor Khan Jan 16 '22 at 07:24

8 Answers8

108

The best way is use an interface:

  1. Declare an interface in the nest fragment

    // Container Activity or Fragment must implement this interface
    public interface OnPlayerSelectionSetListener
    {
        public void onPlayerSelectionSet(List<Player> players_ist);
    }
    
  2. Attach the interface to parent fragment

    // In the child fragment.
    public void onAttachToParentFragment(Fragment fragment)
    {
        try
        {
            mOnPlayerSelectionSetListener = (OnPlayerSelectionSetListener)fragment;
    
        }
        catch (ClassCastException e)
        {
              throw new ClassCastException(
                  fragment.toString() + " must implement OnPlayerSelectionSetListener");
        }
    }
    
    
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        Log.i(TAG, "onCreate");
        super.onCreate(savedInstanceState);
    
        onAttachToParentFragment(getParentFragment());
    
        // ...
    }
    
  3. Call the listener on button click.

    // In the child fragment.
    @Override
    public void onClick(View v)
    {
        switch (v.getId())
        {
            case R.id.tv_submit:
                if (mOnPlayerSelectionSetListener != null)
                {                
                     mOnPlayerSelectionSetListener.onPlayerSelectionSet(selectedPlayers);
                }
                break;
            }
        }
    
  4. Have your parent fragment implement the interface.

     public class Fragment_Parent extends Fragment implements Nested_Fragment.OnPlayerSelectionSetListener
     {
          // ...
          @Override
          public void onPlayerSelectionSet(final List<Player> players_list)
          {
               FragmentManager fragmentManager = getChildFragmentManager();
               SomeOtherNestFrag someOtherNestFrag = (SomeOtherNestFrag)fragmentManager.findFragmentByTag("Some fragment tag");
               //Tag of your fragment which you should use when you add
    
               if(someOtherNestFrag != null)
               {
                    // your some other frag need to provide some data back based on views.
                    SomeData somedata = someOtherNestFrag.getSomeData();
                    // it can be a string, or int, or some custom java object.
               }
          }
     }
    

Add Tag when you do fragment transaction so you can look it up afterward to call its method. FragmentTransaction

This is the proper way to handle communication between fragment and nest fragment, it's almost the same for activity and fragment. http://developer.android.com/guide/components/fragments.html#EventCallbacks

There is actually another official way, it's using activity result, but this one is good enough and common.

uDevel
  • 1,891
  • 1
  • 13
  • 14
  • 2
    getParentFragment() only works in API 17+, so this answer should explicit this, or it isn't clear enough – cesards Nov 11 '14 at 16:16
  • @uDevel What does `onAttachFragment(getParentFragment());` (in step 2) do? Is it in the `Activity`? – Antonio Sesto Mar 29 '15 at 10:42
  • @antonio, no, it's in the nested fragment. The point of it is to check if the parent fragment has implemented the listener so that you can interact with parent fragment from the nested fragment by calling the method defined in the listener interface. – uDevel Mar 29 '15 at 14:18
  • For some reason, I cannot access onAttachFragment from v4 support library even though it is listed in documentation. – ShrimpCrackers Aug 02 '16 at 22:38
  • 8
    @ShrimpCrackers there may be some confusion because since `Support Library v24.0.0` or API 24 in native Android, the Google engineers introduced a new method called [**onAttachFragment(Fragment)**](https://developer.android.com/reference/android/app/Fragment.html#onAttachFragment(android.app.Fragment)) which actually does something slightly different from the one in this answer here. uDevei just happened to choose the same method name when he wrote this answer 2 years ago. To be clear: you are not overriding the `onAttachFragment()` in the Android framework, you are **implementing** your own. – Tony Chan Sep 13 '16 at 18:28
  • 1
    @TonyChan Thanks for clarifying the confusion. Since Google created a new method with the same name, I want to update this answer with a better method name, but I can't think of a less confusing name. The one from Google is actually named improperly, imo. By reading on what it does, it should be called onChildFragmentAttached instead. – uDevel Oct 04 '16 at 16:39
  • 1
    Update method name to onAttachToParentFragment to avoid confusion with Google's method. – uDevel Feb 02 '17 at 19:08
  • getParentFragment() was the missing piece for me! – Mofe Ejegi Nov 09 '18 at 09:27
  • **Step 2** could well be replaced by the `onAttach(@NonNull Context context)` in the `ChildFragment`'s method, by implementing: `mOnPlayerSelectionSetListener = (OnPlayerSelectionSetListener) getParentFragment();` – Aliton Oliveira May 19 '20 at 23:52
  • But isn't this also a strong coupling of child and parent fragments, what if you want to reuse that child fragment in some activity? Can this be done via the event bus to remove a strong coupling between them? Or just go with a single activity app where you are sure that parent will always be fragment? – uberchilly Jul 30 '20 at 08:48
35

Instead of using interface, you can call the child fragment through below:

( (YourFragmentName) getParentFragment() ).yourMethodName();
Vidhi Dave
  • 5,614
  • 2
  • 33
  • 55
Raja Jawahar
  • 6,742
  • 9
  • 45
  • 56
  • 5
    This should be the accepted answer IMHO. It's one line of code and does exactly what is needed. Wish I could upvote it more – Sound Conception Aug 22 '15 at 01:54
  • 54
    You should avoid this solution if possible. The whole point of a fragment is that it is a re-usable section of UI. If your child fragment contains a hard-coded reference to the parent fragment, then you lose your re-usability of the child fragment. That's why interfaces are great, the parent will choose how to implement them. – Mike Baxter Sep 18 '15 at 10:52
  • 1
    There is not a case this is a good solution other than being lazy and not using fragment correctly. If the child fragment know what class the parent fragment is, then you probably shouldn't create the child fragment, and just put the code in the parent fragment. – uDevel May 26 '16 at 18:49
  • where should we use this? either in parent or child? – Uma Achanta Oct 06 '16 at 11:20
  • 3
    @uDevel Sometimes you have to use child Fragments - let's say you want to have tabs inside your parent Fragment. In that case, the child Fragments were probably made to work with only the parent Fragment. I guess in that case the one line solution is great. – rozina Dec 20 '16 at 14:47
  • @rozina I think you are right. There are cases where we are forced to use fragment. – uDevel Dec 22 '16 at 23:03
  • There's a typo. P should be capital in getparentFragment(). – Ganesh Mohan Jun 02 '17 at 12:27
  • Sorry.. @ganesh2shiv – Raja Jawahar Jun 02 '17 at 13:36
  • 3
    This is not a good solution .Your child fragment get tightly coupled with your parent fragment – Farmaan Elahi Nov 19 '17 at 16:36
4

The best way to pass data between fragments is using Interface. Here's what you need to do: In you nested fragment:

public interface OnDataPass {
    public void OnDataPass(int i);
}

OnDataPass dataPasser;

@Override
public void onAttach(Activity a) {
    super.onAttach(a);
    dataPasser = (OnDataPass) a;
}

public void passData(int i) {
    dataPasser.OnDataPass(i);
}

In your parent fragment:

public class Fragment_Parent extends Fragment implements OnDataPass {
...

    @Override
    public void OnDataPass(int i) {
        this.input = i;
    }

    btnOk.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            Fragment fragment = getSupportFragmentManager().findFragmentByTag("0");
            ((Fragment_Fr1) fragment).passData();
        }
    }

}
Gabriel Bourgault
  • 842
  • 11
  • 22
Dariush Malek
  • 914
  • 1
  • 6
  • 14
  • `onAttach(Activity a)` is now **depricated**. Instead I implemented `onAttach(@NonNull Context context)`, but `dataPasser = (OnDataPass) context;` gives me an FATAL EXCEPTION: **java.lang.ClassCastException: com.myapp.MainActivity cannot be cast to com.myapp.OnDataPass**. – Aliton Oliveira May 19 '20 at 23:35
  • It worked when I defined: `dataPasser = (OnDataPass) getParentFragment();`. Is it bad? – Aliton Oliveira May 19 '20 at 23:41
4

You can use share data between fragments.

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}


public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, item -> {
           // Update the UI.
        });
    }
}

More Info ViewModel Architecture

Ahmad Aghazadeh
  • 16,571
  • 12
  • 101
  • 98
3

You can use getChildFragmentManager() and find nested fragments, get them and run some methods to retrieve input values

michal.luszczuk
  • 2,883
  • 1
  • 15
  • 22
  • I use the childFragmentManager already to submit the transactions initially in my parent fragment; however when I try to call getChildFragmentManager() in some of my parent fragments' methods it returns an error. – Timothy Frisch Apr 17 '14 at 20:40
  • so FR1 has method "constructFRaProfile()"; which I call after I hit submit in FR1. Should I call childFragmentManager().findFragmentByTag("FRa");? and then start retrieving values? – Timothy Frisch Apr 17 '14 at 20:44
  • I am not sure if I set my tags correctly; does android automatically set a tag for a child fragment or must it be manual? – Timothy Frisch Apr 17 '14 at 20:44
  • How you are using your child fragments? Adding from code or using them in XML layout – michal.luszczuk Apr 17 '14 at 20:45
  • @Jedil you can't add child fragments in XML, they have to be added programatically – starkej2 Apr 17 '14 at 20:46
  • Android only supports nested fragments dynamically from code so they are added to a framelayout from code. What @blacksh33p said. – Timothy Frisch Apr 17 '14 at 20:46
  • @Tukajo what is the specific error you are getting? – starkej2 Apr 17 '14 at 20:47
  • It was just a typo @blacksh33p I fixed that problem, I am not sure how to set and get a tag I guess to make sure I am referring to the correct fragments when retrieving data. – Timothy Frisch Apr 17 '14 at 20:49
  • I usually define a constant for the fragment tag to prevent errors from typos – starkej2 Apr 17 '14 at 20:51
  • Plus @blacksh33p; if I am replacing the fragments by using my FR1's various buttons, if I try to retrieve those values with a submit button wouldn't the values of the other nested fragments be unobtainable since they are not the currently active nested fragment – Timothy Frisch Apr 17 '14 at 20:51
  • I will add some code to describe further. @blacksh33p – Timothy Frisch Apr 17 '14 at 20:51
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/50918/discussion-between-blacksh33p-and-tukajo) – starkej2 Apr 17 '14 at 20:52
3

Check for instanceOf before getting parent fragment which is better:

if (getParentFragment() instanceof ParentFragmentName) {
  getParentFragment().Your_parent_fragment_method();
}
Pankaj
  • 7,908
  • 6
  • 42
  • 65
3

Passing data between fragments can be done with FragmentManager. Starting with Fragment 1.3.0-alpha04, we can use setFragmentResultListener() and setFragmentResult() API to share data between fragments.

Official Documentation

Nanzbz
  • 199
  • 2
  • 5
0

Too late to ans bt i can suggest create EditText object in child fragment

EditText tx;

in Oncreateview Initialize it. then create another class for bridge like

public class bridge{
public static EditText text = null; 
}

Now in parent fragment get its refrence.

EditText childedtx = bridge.text;

now on click method get value

onclick(view v){
childedtx.getText().tostring();
}

Tested in my project and its work like charm.

Rachit Vohera
  • 707
  • 7
  • 10