Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Communication between nested fragments in Android

I recently learned how to make nested fragments in Android. I don't know how communication is supposed to happen, though.

enter image description here

From reading the fragment communication documentation I know that

All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.

This makes sense for sibling fragments within an activity, but it doesn't make as much sense for parent-child fragment communication. Do I need to go all the way up to the Activity just for the Child Fragment to talk to the Parent Fragment? If the answer is a simple "yes" then I can do that. If it is a "no", then what would the code design look like?

I see in the Nested Fragment documentation that one can use getParentFragment() to get a reference to the parent fragment. So does that mean that the child should directly communicate with the parent? That seems opposite from what is encouraged with a normal fragment communicating with the parent activity.

like image 405
Suragch Avatar asked Sep 14 '16 13:09

Suragch


People also ask

How can two fragments communicate Android?

We can communicate within fragments using the ViewModel. We can also communicate between fragments using Interface.

What is the use of FragmentManager in Android?

FragmentManager is the class responsible for performing actions on your app's fragments, such as adding, removing, or replacing them, and adding them to the back stack.


2 Answers

Although @Suragch's answer is correct, But I want to add another way to pass data between Fragments or Activity. No matter it's an Activity or Fragment you can pass data with event bus in 3 steps:

1- Define an event(message):

public class OrderMessage {     private final long orderId;     /* Additional fields if needed */     public OrderMessage(long orderId) {         this.orderId = orderId;     }     public long getOrderId() {         return orderId;     } } 

2- Register and Unregister for Events: To be able to receive events, the class must register/unregister for event bus. The best place for Activities and Fragments is onStart() and onStop()

@Override public void onStart() {     super.onStart();     EventBus.getDefault().register(this); }  @Override public void onStop() {     EventBus.getDefault().unregister(this);     super.onStop(); } 

To be able to receive an event you have to subscribe to that event. To do that, add @Subscribe annotation to one of your methods in your class.

@Subscribe(threadMode = ThreadMode.MAIN)    public void onMessage(OrderMessage message){        /* Do something for example: */        getContractDetails(message.getOrderId());    } 

3- Post an event

EventBus.getDefault().post(new OrderMessage(recievedDataFromWeb.getOrderId())); 

More documentation and examples could be found Here. There are also other libraries like : Otto

like image 37
Milad Faridnia Avatar answered Oct 02 '22 15:10

Milad Faridnia


Following Rahul Sharma's advice in the comments, I used interface callbacks to communicate up from the Child Fragment to the Parent Fragment and to the Activity. I also submitted this answer to Code Review. I am taking the non-answer there (at the time of this writing) to be a sign that there are no major problems with this design pattern. It seems to me to be consistent with the general guidance given in the official fragment communication docs.

Example project

The following example project expands the example given in the question. It has buttons that initiate upward communication from the fragments to the activity and from the Child Fragment to the Parent Fragment.

I set up the project layout like this:

enter image description here

Main Activity

The Activity implements the listeners from both fragments so that it can get messages from them.

Optional TODO: If the Activity wanted to initiate communication with the fragments, it could just get a direct reference to them and then call one of their public methods.

import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log;  public class MainActivity extends AppCompatActivity implements ParentFragment.OnFragmentInteractionListener, ChildFragment.OnChildFragmentToActivityInteractionListener {      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);          FragmentTransaction ft = getSupportFragmentManager().beginTransaction();         ft.replace(R.id.parent_fragment_container, new ParentFragment());         ft.commit();     }      @Override     public void messageFromParentFragmentToActivity(String myString) {         Log.i("TAG", myString);     }      @Override     public void messageFromChildFragmentToActivity(String myString) {         Log.i("TAG", myString);     } } 

Parent Fragment

The Parent Fragment implements the listener from the Child Fragment so that it can receive messages from it.

Optional TODO: If the Parent Fragment wanted to initiate communication with the Child Fragment, it could just get a direct reference to it and then call one of its public methods.

import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup;   public class ParentFragment extends Fragment implements View.OnClickListener, ChildFragment.OnChildFragmentInteractionListener {       // **************** start interesting part ************************      private OnFragmentInteractionListener mListener;       @Override     public void onClick(View v) {         mListener.messageFromParentFragmentToActivity("I am the parent fragment.");     }      @Override     public void messageFromChildToParent(String myString) {         Log.i("TAG", myString);     }      public interface OnFragmentInteractionListener {         void messageFromParentFragmentToActivity(String myString);     }      // **************** end interesting part ************************        @Override     public void onAttach(Context context) {         super.onAttach(context);         if (context instanceof OnFragmentInteractionListener) {             mListener = (OnFragmentInteractionListener) context;         } else {             throw new RuntimeException(context.toString()                     + " must implement OnFragmentInteractionListener");         }     }      @Override     public View onCreateView(LayoutInflater inflater, ViewGroup container,                              Bundle savedInstanceState) {         View view = inflater.inflate(R.layout.fragment_parent, container, false);         view.findViewById(R.id.parent_fragment_button).setOnClickListener(this);         return view;     }      @Override     public void onViewCreated(View view, Bundle savedInstanceState) {         Fragment childFragment = new ChildFragment();         FragmentTransaction transaction = getChildFragmentManager().beginTransaction();         transaction.replace(R.id.child_fragment_container, childFragment).commit();     }      @Override     public void onDetach() {         super.onDetach();         mListener = null;     }  } 

Child Fragment

The Child Fragment defines listener interfaces for both the Activity and for the Parent Fragment. If the Child Fragment only needed to communicate with one of them, then the other interface could be removed.

import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup;   public class ChildFragment extends Fragment implements View.OnClickListener {       // **************** start interesting part ************************      private OnChildFragmentToActivityInteractionListener mActivityListener;     private OnChildFragmentInteractionListener mParentListener;      @Override     public void onClick(View v) {         switch (v.getId()) {             case R.id.child_fragment_contact_activity_button:                 mActivityListener.messageFromChildFragmentToActivity("Hello, Activity. I am the child fragment.");                 break;             case R.id.child_fragment_contact_parent_button:                 mParentListener.messageFromChildToParent("Hello, parent. I am your child.");                 break;         }     }      public interface OnChildFragmentToActivityInteractionListener {         void messageFromChildFragmentToActivity(String myString);     }      public interface OnChildFragmentInteractionListener {         void messageFromChildToParent(String myString);     }      @Override     public void onAttach(Context context) {         super.onAttach(context);          // check if Activity implements listener         if (context instanceof OnChildFragmentToActivityInteractionListener) {             mActivityListener = (OnChildFragmentToActivityInteractionListener) context;         } else {             throw new RuntimeException(context.toString()                     + " must implement OnChildFragmentToActivityInteractionListener");         }          // check if parent Fragment implements listener         if (getParentFragment() instanceof OnChildFragmentInteractionListener) {             mParentListener = (OnChildFragmentInteractionListener) getParentFragment();         } else {             throw new RuntimeException("The parent fragment must implement OnChildFragmentInteractionListener");         }     }      // **************** end interesting part ************************        @Override     public View onCreateView(LayoutInflater inflater, ViewGroup container,                              Bundle savedInstanceState) {         View view = inflater.inflate(R.layout.fragment_child, container, false);         view.findViewById(R.id.child_fragment_contact_activity_button).setOnClickListener(this);         view.findViewById(R.id.child_fragment_contact_parent_button).setOnClickListener(this);         return view;     }      @Override     public void onDetach() {         super.onDetach();         mActivityListener = null;         mParentListener = null;     }  } 
like image 142
Suragch Avatar answered Oct 02 '22 17:10

Suragch