How can I pass data between a fragment and its container activity? Is there something similar to passing data between activities through intents?

I read this, but it didn't help much:
http://developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity

upvote
  flag
Is that insufficient? You can send anything you want, maybe you can explain more, what do you want to accomplish? – Marcin Milejski
upvote
  flag
From the document I didn't understand how, for example, I would pass a value or a string from the activity to the fragment or vice-versa. Thanks – tyb
1 upvote
  flag
The best way to handle asynchronously calls from fragments and return them data is to implement BroadcastReceivers on both sides. You should anyway make it asynchronous if you're working with non-specific number of fragments. – Marek Sebera
upvote
  flag
Link in question is broken – punisher_malade
upvote
  flag
@punisher_malade No, works for me – Vadim Kotov

10 Answers 11

up vote 171 down vote accepted

In your fragment you can call getActivity().

This will give you access to the activity that created the fragment. From there you can obviously call any sort of accessor methods that are in the activity.

e.g. for a method called getResult() on your Activity:

((MyActivity) getActivity()).getResult();
upvote
  flag
If i had a method in the activity called getResult() would i call it by writing getActivity().getResult()? that doesnt work. Thanks – tyb
79 upvote
  flag
Since you are accessing a function within YOUR Activity (and not the parent Android activity) you will need to cast your getActivity() call: ((MyActivity) getActivity()).getResult(); – Nick
2 upvote
  flag
How will be possibly the back method, from fragment to activity? – Vasil Valchev
6 upvote
  flag
@VasilValchev, you could create an interface and force the activity to implement it and then call a method from within your fragment to pass the data. Use the onAttach method to check whether the activity implements the interface. – Ivan Nikolov
3 upvote
  flag
Excellent answer from @IvanNikolov. You can find a thorough explanation at the Fragments Training Link – bogdan
upvote
  flag
In such method you statically link your fragment with the one activity descendant. If you want to use fragment from more than one activity, you need to use the second answer with interfaces. – Artem Mostyaev

Try using interfaces.

Any fragment that should pass data back to its containing activity should declare an interface to handle and pass the data. Then make sure your containing activity implements those interfaces. For example:

In your fragment, declare the interface...

public interface OnDataPass {
    public void onDataPass(String data);
}

Then, connect the containing class' implementation of the interface to the fragment in the onAttach method, like so:

OnDataPass dataPasser;

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    dataPasser = (OnDataPass) context;
}

Within your fragment, when you need to handle the passing of data, just call it on the dataPasser object:

public void passData(String data) {
    dataPasser.onDataPass(data);
}

Finally, in your containing activity which implements OnDataPass...

@Override
public void onDataPass(String data) {
    Log.d("LOG","hello " + data);
}
1 upvote
  flag
Thanks good answer, to the point. This is also very nicely explained here. What I didn't realize is that you can implement multiple interfaces, I was already implementing a ActionBar.TabListener and had to add an additional interface. – Eugene van der Merwe
3 upvote
  flag
This is definitely the way to go and in my opinion the ONLY way to go. And provides two way interaction between the Activity and the Fragment. Also will allow a means to communicate between fragments when you have multiple fragments in one activity whether through tabs or multi frag layout. – Christopher
upvote
  flag
I create a BaseFragmetn as abstract and add a abstract method onDataPassed() and All my fragments extends this BaseFragment. When passing data is needed,just find the fragment and call onDataPassed is OK. May Help. – acntwww
1 upvote
  flag
There is something I'm wondering... This is the official answer, and from the official developer site they say this is the right way to comunicate frag-act-frag, but why is it even possible to do it via casting getActivity()? – unmultimedio
3 upvote
  flag
Why does somebody need an extra interface to do that? Why do you consider the following solution bad or your solution better? ((MyActivity) getActivity).myMethod(...) – j0n0
upvote
  flag
kindly please let me know why in your second code snippet you did the following "dataPasser = (OnDataPass) a;" ?? why you assigned a reference of the interface to an activity?? – rmaik
upvote
  flag
onAttach(Activity a) is depricated – Milad Faridnia
upvote
  flag
what about having 5 fragments ina tab like setting, i'm sure you would declare a different interface for each fragment or is there a betterway? – Tanner Summers
upvote
  flag
this is known as Callbacks – user25
2 upvote
  flag
onAttach(Activity activity) is deprecated, please using onAttach(Context context) instead. – Chris.C
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    Bundle b = getActivity().getIntent().getExtras();
            wid = b.getString("wid");
            rid = b.getString("rid");
            View view = inflater.inflate(R.layout.categoryfragment, container, false);
    return view;
 }
11 upvote
  flag
This is how to get it in the Fragment, But how do you set it in the class calling the fragment if you have your fragment in xml layout file. – Zapnologica

Another simple way to get datas, passed from another activity, in a fragment in a container activity : for example :

Activity_A => Activity_B(Fragment)

In your Activity_A you create an intent like you're sending a data (String here) to another activity :

Intent intent = new Intent(getBaseContext(),Activity_B.class);
intent.putExtra("NAME", "Value");
startActivity(intent);

in your Fragment, contained in your Activity_B :

String data = getActivity().getIntent().getExtras();
upvote
  flag
getBaseContext() gives me the following error: The method getBaseContext() is undefined for the type new View.OnClickListener(){} – Si8

Passing data between a fragment and its container activity

Activity:

        Bundle bundle = new Bundle();
        bundle.putString("message", "Alo Elena!");
        FragmentClass fragInfo = new FragmentClass();
        fragInfo.setArguments(bundle);
        transaction.replace(R.id.fragment_single, fragInfo);
        transaction.commit();

Fragment:

Reading the value in the fragment

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        String myValue = this.getArguments().getString("message");
        ...
        ...
        ...
        }

Easiest Approach but not Recommended

You can access activity data from fragment:

Activity:

public class MyActivity extends Activity {

    private String myString = "hello";

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

    public String getMyData() {
        return myString;
    }
}

Fragment:

public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        MyActivity activity = (MyActivity) getActivity();
        String myDataFromActivity = activity.getMyData();
        return view;
    }
}
3 upvote
  flag
This adds high coupling to your code and is a bad practice. Now you cannot use the Fragment in any Activity other than MyActivity – AmeyaB
upvote
  flag
why this way is consider as bad practice and is interface is only solution of this question? – androidXP
upvote
  flag
AmeyaB if all activity extends from a BaseActivity or and we cast it like BaseActivity activity = (BaseActivity) getActivity(); then it will work on all activities – Nepster

I used an AppCompatActivity that implements Date Listeners. Fragments came as a necessity since I needed to code a date range selector. And I also needed the container to receive the selected dates to return them to the parent activity.

For the container activity, this is the class declaration:

public class AppCompatDateRange extends AppCompatActivity implements
    DateIniRangeFragment.OnDateIniSelectedListener, DateFimRangeFragment.OnDateFimSelectedListener

And the interfaces for the callbacks:

@Override
public void onDateIniSelected(String dataIni) {
    Log.i("data inicial:", dataIni);
}

@Override
public void onDateFimSelected(String dataFim) {
    Log.i("data final:", dataFim);
}

The callbacks are strings because dates are params in an query select.

The code for the fragments (based on the initial date fragment):

public class DateIniRangeFragment extends Fragment {
OnDateIniSelectedListener callbackIni;

private DatePicker startDatePicker;

public DateIniRangeFragment() {
    ///required empty constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

///through this interface the fragment sends data to the container activity
public interface OnDateIniSelectedListener {
    void onDateIniSelected(String dataIni);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ///layout for the fragment
    View v = inflater.inflate(R.layout.date_ini_fragment, container, false);

    ///initial date for the picker, in this case, current date
    startDatePicker = (DatePicker) v.findViewById(R.id.start_date_picker_appcompat);
    Calendar c = Calendar.getInstance();
    int ano = c.get(Calendar.YEAR);
    int mes = c.get(Calendar.MONTH);
    int dia = c.get(Calendar.DAY_OF_MONTH);
    startDatePicker.setSpinnersShown(false);
    startDatePicker.init(ano, mes, dia, dateSetListener);

    return v;
}

///listener that receives the selected date
private DatePicker.OnDateChangedListener dateSetListener = new DatePicker.OnDateChangedListener() {
    public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        if (view.isShown()) { ///if the datepicker is on the screen
            String sDataIni = year + "-" + (monthOfYear + 1) + "-" + dayOfMonth;
            callbackIni.onDateIniSelected(sDataIni); //apply date to callback, string format
        }
    }
};

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    /*
    * this function guarantees that the container activity implemented the callback interface
    * */
    try {
        callbackIni = (OnDateIniSelectedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " deve implementar OnDateIniSelectedListener");
    }
}

}

To compose the container + fragments, I used an ViewPager (AppCompat) with a custom class that extends FragmentPagerAdapter. No dialogs.

I don't know if this is the best way or not Bu I have been searching on google for quite a while finding how can I pass a Bundle from a fragment to its container activity but all I found was sending data from activity to fragment instead (which was a bit confusing for me as I'm a newbie).

later I tried something own my own that exactly worked for me as I wanted. so I'll post it here case someone like me looking for the same thing.

// Passing data from Fragment .

Bundle gameData = new Bundle();
        gameData.putStringArrayList(Constant.KEY_PLAYERS_ARR,players);
        gameData.putString(Constant.KEY_TEAM_NAME,custom_team_name);
        gameData.putInt(Constant.KEY_REQUESTED_OVER,requestedOver);

        Intent intent = getActivity().getIntent();
        intent.putExtras(gameData);

// Getting data from the bundle from it's container activity .

Bundle gameData = getIntent().getExtras();
        if (gameData != null)
        {
            int over = gameData.getInt(Constant.KEY_REQUESTED_OVER);
            ArrayList<String> players = gameData.getStringArrayList(Constant.KEY_PLAYERS_ARR);
            String team = gameData.getString(Constant.KEY_TEAM_NAME);

        }
        else if (gameData == null)
        {
            Toast.makeText(this, "Bundle is null", Toast.LENGTH_SHORT).show();
        }

Interface is one of the best solutions:

Glue Interface:

public interface DataProviderFromActivity {

    public String getName();
    public String getId);

}  

MyActivity:

public class MyActivity implements DataProviderFromActivity{

    String name = "Makarov";
    String id = "sys533";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    public String getName(){
        return name;
    };
    public String getId(){
        return id;
    };
}

MyFragment:

public class MyFragment extends Fragment{

    String fragName = "";
    String fragId = "";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        DataProviderFromActivity myActivity= (DataProviderFromActivity) getActivity();
        fragName = myActivity.getName();
        fragId = myActivity.getId();

        ... ... ... ... ... .... .... 
        ... ... ... ... ... .... .... 

        updateFragmentView();
    }
}
upvote
  flag
Hi @HassanMakarov I like your interface, simple & clean. I do have one issue though. I don't know if it's specific to my viewpager but I only get the data in at most 2 fragments at a time? The strings "Makarov" & "sys533" appear in fragments 1 & 2 when the activity is created but the strings don't appear in fragment 3 until fragment 2 or 3 is selected? Any ideas? – JC23
upvote
  flag
@JC23 I guess the problem is related to fragment transaction. Which fragment is set when MainActivity is launched? – Hassan Tareq
upvote
  flag
@JC23 What I do: instead of Tab/ViewPager I use Toolbar & NavigationView. In onNavigationItemSelected() I perform fragment transactions to set fragments accordingly. – Hassan Tareq

Simply you can use EventBus it is easy and work great

EventBus in 3 steps

  1. Define events:

    public static class MessageEvent { /* Additional fields if needed */ }

  2. Prepare subscribers: Declare and annotate your subscribing method, optionally specify a thread mode:

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {/* Do something */};

Register and unregister your subscriber. For example on Android, activities and fragments should usually register according to their life cycle:

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

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }
  1. Post events:

    EventBus.getDefault().post(new MessageEvent());

Not the answer you're looking for? Browse other questions tagged or ask your own question.