2

I have this Activity (Foo) that hosts this Fragment (Bar) which is filled with information coming from another Activity.

The process goes like this:

MainActivity → Bundle → Foo → Bar → gather data and return to MainActivity.

  • Should I store that information in private fields of the Foo Activity at onCreate, and just making the Fragment use them?
  • Should I pass the Bundle directly to my Bar Fragment as arguments?
  • Should I make a new Bundle out of the received one and pass the new Bundle to the Fragment?
  • Should I completely replace the whole fragment each time data changes or should I only update the changing views, when data isn't directly being provided by the user e.g. by a Dialog?

The fragment only has one view whose value may be changed, but this value has to be formatted (it's received from a Dialog).

  • Should I store only that changing value in a private field of my Activity and access it when I have to update the view or retrieve the result of the Activity when it finishes?
  • Should I implement an Adapter on such view so it stores the value, but shows the formatted information to the user?
  • Should I store the formatted value in the view and parse it back to the raw value I need to receive the information it contains when I have to return the gathered data?
  • Should I store all the information gathered, replace the fragment for a new one with the information previously collected (by using one of the methods in the first section)?

Is there a convention that covers passing data from one activity to another, and how the data may be used through the lifecyle of the second activity? Or at least some kind of best practices for this?

EDIT:

I'm aware of how to do all of the above points, so I'm not asking for code; the thing is that I find a lack of homogeneity between how I pass the data from the main activity to the child activity, how I pass it to the fragment and how do I update the fragment in the case that data is changed.

arielnmz
  • 8,354
  • 9
  • 38
  • 66
  • I don't fully understand You problem with changing view's data. Is it fragment's view or Activity's view? You get new data/change data in Activity or in Fragment? – Gustek Jun 12 '14 at 01:52
  • It's a view inside my fragment. – arielnmz Jun 12 '14 at 02:40

3 Answers3

2

I think the method that you are looking for to pass data from Fragment bar to Activity foo is called onAttach. This method gets called when the activity launches the fragment for the first time and passes to the fragment itself. Create an interface for the activity that the fragment uses to pass data back to the Activity. The activity can pass data to the fragment directly.

  • the thing is that I find a lack of homogeneity between how I pass the data to the activity

    You pass data to the activity from an fragment through an interface

  • how I pass it to the fragment

    Pass data to the fragment directly through public methods (that is if the fragment is alive. If you are instantiating one, use Bundle and Arguments)

  • and how do I update the fragment in the case that data is changed

    If a value changed in the activity and you want to notify the fragment to display something new, create a public method and call that from the activity. mFragment.updateView();

I know you didn't ask for code, but this is the basic concept.

Interface:

public interface MyActivityInterface {

    public void passInformationToActivity(Object obj);

}

Activity:

public class MyActivity implements MyActivityInterface {

    private MyFragment myFragment;

    @Override
    public void passInformationToActivity(Object obj) {
        Log.e(TAG, "Information was passed to the activity");
    }

    public void sendInformationToFragment(Object obj) {
        myFragment.sendInformation(obj);
    }
}

Fragment:

public class MyFragment extends Fragment {

    private MyActivityInterface mInterface;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mInterface = (MyActivityInterface) activity;
        catch (ClassCastException e) {
            Log.e(TAG, "Calling activity must implement MyActivityInterface");
            throw e;
        }
    }

    @Override
    public void onDetatch() {
        super.onDetatch();
        mInterface = null;
    }

    private void notifyActivityOfObject(Object obj) {
        mInterface.passInformationToActivity(obj);
    }

    public void sendInformation(Object obj) {
        Log.e(TAG, "Object received by Fragment");
    }
}

The fragment can talk to the activity through MyActivityInterface and the Activity can talk to the fragment by calling public methods directly on it.

Hope this helps/answers your question. If not, I'm happy to try to help more

dsrees
  • 6,116
  • 2
  • 26
  • 26
  • Thank you for your response, but regarding the part of the communication activity → fragment, should I then have a field holding a reference to the actual fragment right? This problem is not very complicated though, I just need to instantiate a fragment then retrieve its information when the activity finishes. Only one view of the activity may chang and I need to keep track of that value until the activity finishies. Should I keep that value on a field of the activity? or should I keep it on the view itself and parse it back when the activity finishes? – arielnmz Jun 12 '14 at 00:06
  • And regarding the fragment creation, should I pass the same bundle I receive from the MainActivity or should I create another bundle with the same data in order to pass it to the fragment? – arielnmz Jun 12 '14 at 00:07
  • Yes, keep a reference to the fragment in the activity class. Keep your data stored in your activity because the activity is housing the fragment. If you need to get information from the fragment once the activity ends, in the `onPause` method of the Activity, call `mFragment.getDataThatINeed();`. As for the bundle, I believe you need to create a new one, but you might want to play around with that – dsrees Jun 12 '14 at 15:45
2

For Activity --> AnotherActivity

You use Intents

For Activity --> Fragment

On creation You use setArguments() it is good practice to use newInstance() method like

class MyFragment extends Fragment {

    private static final String ARG_1 = "arg1";
    private static final String ARG_2 = "arg2";

    public static MyFragment newInstance(String arg1, int arg2) {
        MyFragment fragment = new MyFragment();
        Bundle args = new Bundle();
        args.putString(ARG_1, arg1);
        args.putInt(ARG_2, arg2);
        fragment.setArguments(args);
        return fragment;
    }
    public MyFragment() {
        // Required empty public constructor
    }
}

To send something to existing fragment, You get reference to this fragment from FragmentManager or keep it in class scope variable in Activity. You define public method in fragment to be called.

For example:

class MyFragment extends Fragment {

    public static final String TAG = "my_fragment_tag";

    public void updateSomething(String newValue) {
        //do what you need here
    }
}



class MyActivity extends FragmentActivity {

    private MyFragment myFragment;

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

        FragmentManager fragmentManager = getSupportFragmentManager();
        myFragment = fragmentManager.findFragmentByTag(MyFragment.TAG);
        //if it is not null there was configuration change and fragment got recreated
        //use fragment lifecycle methods to get correct state
        if(myFragment==null) {
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

            myFragment = MyFragment.newInstance("arg1",2);

            fragmentTransaction.add(R.id.main, myFragment, MyFragment.TAG);

            fragmentTransaction.commit();
        }

    }
}

In this example I keep reference to MyFragment in myFragment so I can call myFragment.updateSomething() if needed from Activity.

For Fragment -> Activity

Use interface

class MyFragment extends Fragment {

    private OnMyFragmentInteractionListener mListener;

    public interface OnMyFragmentInteractionListener {
        public void onMyFragmentInteraction(int arg);
    }


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

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    public void onSomethinfHappend(int arg) {
        mListener.onMyFragmentInteraction(arg);
    }
}

class MyActivity extends FragmentActivity 
    implements MyFragment.OnMyFragmentInteractionListener {

    public void onMyFragmentInteraction(int arg) {
        //receive message from MyFragment
    }
}

For Fragment --> AnotherFragment

You need to go through Activity so You will have

Fragment -> Activity --> AnotherFragment

and You achieve it using methods described above.

Additionally You may startActivityForResult() from fragment and receive result in fragment, but fragments won't receive result if startActivityForResult() was called in Activity. Little more on this here

To answer Your question about Bundle, if this bundle contains data You need to pass to fragment then yes you can use the same Bundle. You can make your newInstance() method to take Bundle as argument or overload it.

Community
  • 1
  • 1
Gustek
  • 3,680
  • 2
  • 22
  • 36
  • I think the main issue of the approach I look for is how data is handled once it has been sent to the activity and to the fragment. I can keep a reference to the current fragment or I can just create a new one everytime data is changed... so as far as I can see there's no other kind of practices to handle the data. Thanks for your suggestions. – arielnmz Jun 12 '14 at 03:44
  • Don't create new one all the time it is pointless and expensive. Just send new data to existing fragment as explained and update it as You need. – Gustek Jun 12 '14 at 12:16
  • It is just like with Activity, You don't recreate it just to update a view. – Gustek Jun 12 '14 at 12:27
  • That's right, but then I'd be holding a reference to the system's bundle and I'm not aware of what the system would do to it after it has been delivered to the activity (though I think it's very unlikely for something like deleting it to happen). You're right, it's pointless and expensive. I guess I now find a pattern here among your answers. – arielnmz Jun 12 '14 at 14:01
  • What do You mean by "system's bundle"? Bundle class is just a container for values You want to pass between activities, send to fragment. Just like with any object it exists as long as there is a reference to it. In practice it means it is tied to activity and fragment you send it to. When they get destroyed Bundle gets removed. – Gustek Jun 12 '14 at 14:44
1

The recommended practice of passing data from activity to activity is through bundle in intents. For Fragment - Activity or Fragment - Fragment communication an interface is the recommended way. Passing bundles to fragment is achieved in two ways.

  1. First fragment.setArguments(bundle); then commit the fragmenttransaction
  2. Second way is by defining a static newInstance() method.

    public class FiveFragment extends Fragment {
    
    public FiveFragment() {
    }
    
    // for id
    public static FiveFragment newInstance(Bundle bundle) {
        FiveFragment myFragment = new FiveFragment();
        myFragment.setArguments(bundle);
        return myFragment;
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        View view = inflater.inflate(R.layout.fragment_five, container, false);
        return view;
    }
    
    @Override
    public void onStart() {
        // TODO Auto-generated method stub
        super.onStart();
    
        if (getArguments() != null) {
            // bundle arguments fetch
            }
        }
    }
    }
    

    Update:

should I create a new Bundle or pass the same one

If the data you need is already in the current bundle I don't see any need to create a new one just for passing it.

If data changes, should I only update the views or should I create a new fragment and replace the previous one.

If the data changes are not so much then updating the view is better. But if most of the data changes then instantiating a new Fragment seems logical to me.

Illegal Argument
  • 10,090
  • 2
  • 44
  • 61
  • Thank you for the bits of information, however, this doesn't address the questions: should I pass directly the same bundle? Or should I create a new one to pass it? If data changes, should I only update the views or should I create a new fragment and replace the previous one? – arielnmz Jun 11 '14 at 17:49