198

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

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
tyb
  • 4,749
  • 11
  • 31
  • 38
  • Is that insufficient? You can send anything you want, maybe you can explain more, what do you want to accomplish? – Marcin Milejski Feb 18 '12 at 17:30
  • 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 Feb 18 '12 at 18:54
  • 2
    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 Jul 20 '13 at 20:42
  • @punisher_malade No, works for me – Vadim Kotov Oct 09 '17 at 11:14

16 Answers16

338

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:

JAVA

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);
}

KOTLIN

Step 1. Create Interface

interface OnDataPass {
    fun onDataPass(data: String)
}

Step 2. Then, connect the containing class' implementation of the interface to the fragment in the onAttach method (YourFragment), like so:

lateinit var dataPasser: OnDataPass

override fun onAttach(context: Context) {
    super.onAttach(context)
    dataPasser = context as OnDataPass
}

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

fun passData(data: String){
    dataPasser.onDataPass(data)
}

Step 4. Finally, in your activity implements OnDataPass

class MyActivity : AppCompatActivity(), OnDataPass {}

override fun onDataPass(data: String) {
    Log.d("LOG","hello " + data)
}
rivaldy
  • 137
  • 3
  • 7
Harlo Holmes
  • 5,145
  • 1
  • 23
  • 20
  • 3
    Thanks good answer, to the point. This is also very nicely explained [here](http://developer.android.com/training/basics/fragments/communicating.html). 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 Aug 17 '12 at 23:53
  • 6
    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 Aug 19 '12 at 14:52
  • 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. – Kevin Dec 21 '13 at 06:36
  • 2
    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 May 16 '14 at 05:24
  • 4
    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(...) – spacifici Nov 10 '14 at 22:05
  • 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 Jan 12 '15 at 11:06
  • onAttach(Activity a) is depricated – Milad Faridnia May 25 '16 at 05:02
  • 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 Jul 06 '16 at 18:57
  • this is known as Callbacks – user25 Sep 03 '16 at 19:08
  • 4
    onAttach(Activity activity) is deprecated, please using onAttach(Context context) instead. – Chris.C Oct 07 '16 at 02:33
  • for some reason for me, `@Override public void onDataPass(String data) { Log.d("LOG","hello " + data); }` the `onDataPass` function can't be found in the containing class / activity – sourlemonaid Dec 06 '18 at 01:16
  • Does anyone know what kind of pattern is followed here? Also what if I have 4-5 fragments all want to pass data to parent activity? In that case parent activity need to implement all these interfaces from all fragments. – IfOnly Apr 24 '19 at 07:04
  • 1
    Hy i tried this solution, but got error like this : ``java.lang.ClassCastException: com.capella.salesapp.ui.MainActivity cannot be cast to com.capella.salesapp.ui.visit.latestvisit.LatestVisitFragment$LatestVisitDataPass at com.capella.salesapp.ui.visit.latestvisit.LatestVisitFragment.onAttach `` I assume its because my container is another fragment not an activity, is there any way to solve this ? – Rifki Maulana Sep 01 '20 at 09:51
  • @RifkiMaulana got the same error. It sounds like the parent activity cannot be casted to the interface. Looking for the solution. – Ehsan Ghasaei Jan 11 '21 at 15:42
  • I agree with this solution. This is a simple interface to make communicate with Fragment and Activity. I try this and success to make that. – Kelvin Herwanda Tandrio Sep 26 '21 at 14:18
  • when opening this fragment from viewpager it gives an classCastException at onAttach method – Karan Malhotra Oct 14 '21 at 07:43
225

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();
Bruno Bieri
  • 9,724
  • 11
  • 63
  • 92
Nick
  • 6,375
  • 5
  • 36
  • 53
  • If i had a method in the activity called getResult() would i call it by writing getActivity().getResult()? that doesnt work. Thanks – tyb Feb 20 '12 at 00:33
  • 86
    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 Feb 21 '12 at 17:20
  • 4
    How will be possibly the back method, from fragment to activity? – Vasil Valchev Dec 10 '12 at 19:39
  • 7
    @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 May 10 '13 at 08:13
  • 4
    Excellent answer from @IvanNikolov. You can find a thorough explanation at the [Fragments Training Link](http://developer.android.com/training/basics/fragments/communicating.html) – bogdan Dec 06 '13 at 12:21
  • 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 Jan 27 '15 at 11:32
  • 11
    This is not a good practice nor pattern. The answer with interfaces is the correct one. – moictab Jan 22 '18 at 09:57
  • This solution encourages really bad practice should be downvoted. For starters, if the Activity using this fragments is not MyActivity, it will cause a ClassCastException. See the solution below using interfaces. – El Sushiboi Oct 15 '20 at 21:25
25

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;
    }
}
Zar E Ahmer
  • 33,936
  • 20
  • 234
  • 300
  • 7
    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 Nov 10 '16 at 00:13
  • why this way is consider as bad practice and is interface is only solution of this question? – androidXP May 18 '17 at 21:51
  • 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 – Zar E Ahmer May 19 '17 at 05:14
  • @Ameya I think you can with a switch or if statement that checks which activity the fragment is called from. – M A F Sep 06 '20 at 17:23
  • @MAF A better approach would be to use an Interface defined in the Fragment or outside. Make the activity implement that and cast the activity to the interface. Now your fragment can be used by any activity that implements the interface. – AmeyaB Sep 16 '20 at 21:49
23
@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;
 }
Tisho
  • 8,320
  • 6
  • 44
  • 52
Jeetu
  • 686
  • 2
  • 10
  • 20
  • 14
    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 Aug 16 '13 at 16:25
17

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");
        ...
        ...
        ...
        }
Jorgesys
  • 124,308
  • 23
  • 334
  • 268
  • hello @Elenasys. How can I send bundle data back to fragment from activity. I went from fragment to activity and I need to go back to fragment which is already created (calls only onStart, onResume,...) and need the data which I was able to obtain in activity. Is there some way how to do it or am I doing something wrong? – Michal Moravik May 26 '19 at 14:11
8

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();
        }
Frozen Diary
  • 81
  • 2
  • 2
7

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();
    }
}
MD TAREQ HASSAN
  • 1,188
  • 20
  • 46
  • 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 Apr 04 '17 at 15:21
  • @JC23 I guess the problem is related to fragment transaction. Which fragment is set when MainActivity is launched? – MD TAREQ HASSAN Apr 05 '17 at 01:56
  • @JC23 What I do: instead of Tab/ViewPager I use Toolbar & NavigationView. In onNavigationItemSelected() I perform fragment transactions to set fragments accordingly. – MD TAREQ HASSAN Apr 05 '17 at 02:05
2

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.

Pablo
  • 361
  • 3
  • 6
2

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());

Sattar
  • 2,453
  • 2
  • 33
  • 47
1

I know this might be late. But I was also always lost on this question. Im sharing this link... because this is possibly the best explanation I ever found on the web for this. This solves Fragment to Activity & Fragment to Fragment!

Solved & Explained really well

Yo Apps
  • 526
  • 9
  • 9
1

From the google official guide:

Starting with Fragment 1.3.0-alpha04, each FragmentManager implements FragmentResultOwner. This means that a FragmentManager can act as a central store for fragment results. This change allows components to communicate with each other by setting fragment results and listening for those results without requiring those components to have direct references to each other.

In Fragment: The child fragment sets the result on its FragmentManager. The parent then receives the result once the fragment is STARTED:

button.setOnClickListener {
    val result = "result"
    // Use the Kotlin extension in the fragment-ktx artifact
    setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}

In the host Activity: To receive a fragment result in the host activity, set a result listener on the fragment manager using getSupportFragmentManager().

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        supportFragmentManager
                .setFragmentResultListener("requestKey", this) { requestKey, bundle ->
            // We use a String here, but any type that can be put in a Bundle is supported
            val result = bundle.getString("bundleKey")
            // Do something with the result
        }
    }
}
Allen
  • 2,979
  • 1
  • 29
  • 34
0

This is working for me..

in Activity add this method

    public void GetData(String data)
     {
        // do something with your data        
     }

and in Fragment add this line

((YourActivity)getContext).GetData("your data here");
paneer_tikka
  • 6,232
  • 1
  • 20
  • 17
Basant
  • 741
  • 7
  • 16
0
public class Fragmentdemo extends Fragment {

  public interface onDemoEventListener {
    public void demoEvent(String s);
  }

  onDemoEventListener demoEventListener;

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

  final String LOG_TAG = "TAG";

  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragmentdemo, null);

    Button button = (Button) v.findViewById(R.id.button);
    button.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        demoEventListener.someEvent("Test text to Fragment1");
      }
    });
    enter code here
    return v;
  }
}
0

First create interface in your Fragment.java

 public interface SendDataInterface{
     public void sendData(String data);
 }

Then create a reference of Activity in Fragment.java by overriding onAttach(Context context) method

@Override
public void onAttach(@NonNull Context context) {
    super.onAttach(context);
    Activity activity =(Activity)context ;
    try{
        mSendDataInterface= (SendDataInterface) activity;
    }catch(RuntimeException e){
        throw new RuntimeException(activity.toString()+" must implement method");
    }
}

Your Fragment.java should look like this :

public class FirstFragment extends Fragment {

private Button mButton;
private EditText mEditText;
SendDataInterface mSendDataInterface;

public FirstFragment(){ }

/**
 * We can not directly access the data of Fragment
 * So we should create an interface
 *
 */
 public interface SendDataInterface{
     public void sendData(String data);
 }


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view =  inflater.inflate(R.layout.fragment_first, container, false);
    mButton = view.findViewById(R.id.button);
    mEditText = view.findViewById(R.id.editText);

    mButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String data= mEditText.getText().toString();
            mSendDataInterface.sendData(data);
        }
    });
    return view;
}

/**
 * We create a reference of activity
 */
@Override
public void onAttach(@NonNull Context context) {
    super.onAttach(context);
    Activity activity =(Activity)context ;
    try{
        mSendDataInterface= (SendDataInterface) activity;
    }catch(RuntimeException e){
        throw new RuntimeException(activity.toString()+" must implement method");
    }
}

Implement SendDataInterface interface in your MainActivity.java

public class MainActivity extends AppCompatActivity implements FirstFragment.SendDataInterface {

private TextView mTextView;

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

    mTextView = findViewById(R.id.textView);

    if(findViewById(R.id.container) != null){

        // Check if app is on Resume in Lifecycle
        if(savedInstanceState != null){
            return;
        }

        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
                .add(R.id.container,new FirstFragment(),null)
                .commit();
    }
}

@Override
public void sendData(String data) {
    mTextView.setText(data);
}
}

Done

UndenCem
  • 21
  • 3
-1

One effective solution I used to convert data from a fragment to an activity is:

In fragment:

Intent intent = new Intent(this, OtherActivity.class);
Bundle bundle = new Bundle();
bundle.putString("KEY", Value);
intent.putExtras(bundle);
getActivity().startActivity(bundle);

//------------------------------------------------------------

In Activity get data:

Bundle bundle = getIntent().getExtras();
if (bundle != null){
String getData = bundle.getString("KEY");
}

Done!
-12

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();
  • `getBaseContext()` gives me the following error: `The method getBaseContext() is undefined for the type new View.OnClickListener(){}` – Si8 Jul 29 '13 at 18:31