Asked  7 Months ago    Answers:  5   Viewed   25 times

I have one activity - MainActivity. Within this activity I have two fragments, both of which I created declaratively within the xml.

I am trying to pass the String of text input by the user into Fragment A to the text view in Fragment B. However, this is proving to be very difficult. Does anyone know how I might achieve this?

I am aware that a fragment can get a reference to it's activity using getActivity(). So I'm guessing I would start there?

 Answers

99

Have a look at the Android developers page: http://developer.android.com/training/basics/fragments/communicating.html#DefineInterface

Basically, you define an interface in your Fragment A, and let your Activity implement that Interface. Now you can call the interface method in your Fragment, and your Activity will receive the event. Now in your activity, you can call your second Fragment to update the textview with the received value

Your Activity implements your interface (See FragmentA below)

public class YourActivity implements FragmentA.TextClicked{
    @Override
    public void sendText(String text){
        // Get Fragment B
        FraB frag = (FragB)
            getSupportFragmentManager().findFragmentById(R.id.fragment_b);
        frag.updateText(text);
    }
}

Fragment A defines an Interface, and calls the method when needed

public class FragA extends Fragment{

    TextClicked mCallback;

    public interface TextClicked{
        public void sendText(String text);
    }

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

        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try {
            mCallback = (TextClicked) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                + " must implement TextClicked");
        }
    }

    public void someMethod(){
        mCallback.sendText("YOUR TEXT");
    }

    @Override
    public void onDetach() {
        mCallback = null; // => avoid leaking, thanks @Deepscorn
        super.onDetach();
    }
}

Fragment B has a public method to do something with the text

public class FragB extends Fragment{

    public void updateText(String text){
        // Here you have it
    }
}
Tuesday, June 1, 2021
 
apokryfos
answered 7 Months ago
19

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

}
Tuesday, July 27, 2021
 
muaaz
answered 5 Months ago
44

I managed to do this. I simply passed the Center panel as a parameter in the North panel's constructor. It works perfectly. Thank you all for the answers :)

Monday, August 23, 2021
 
Grzegorz
answered 4 Months ago
68

Recently the androidx.fragment.app.FragmentManager is deprecated

It is not deprecated at the present time. For example, it is not marked as deprecated in the documentation.

'FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager)' is deprecated

The single-parameter FragmentStatePagerAdapter constructor is deprecated. However, if you read the documentation for that constructor, you will find:

This constructor is deprecated. use FragmentStatePagerAdapter(FragmentManager, int) with BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT

So, replace FragmentStatePagerAdapter(fm) with FragmentStatePagerAdapter(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT), to retain the functionality from the original one-parameter constructor.

Wednesday, October 6, 2021
 
Deepanshu Goyal
answered 2 Months ago
31
public static final int SERVICES_ALL = 1;
public static final int SERVICES_FEATURED = 2;
public static final int SERVICES_NEW = 3;

should be

public static final int SERVICES_ALL = 0;
public static final int SERVICES_FEATURED = 1;
public static final int SERVICES_NEW = 2;

getItem is 0-based-index, which means that in your code, the case 0 of your Adapter never matches one, and in that case you are returning a null fragment causing the exception

Tuesday, October 12, 2021
 
Aximili
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share