3

I have this structure:

Activity A has a ViewPager. The pages of a ViewPager are as known Fragments.

Activity A has a list of complex objects, which I want to display in the pages.

When the user clicks on an item inside the ViewPager activity B must be started. Activity B also needs the list with complex objects.

So I have to pass this list

A -> Fragment(s) -> B

First I found out that it's not possible to use the fragment's constructor to pass data to it. I have to use parcelable or serializable. In the fragment I then have to parcel or serialize again to pass to B.

But I don't want to serialize or parcel whole list of complex data 2 times only to get it through to B.

I would like to avoid using static fields.

Then I came to the idea to pass a listener to the fragment, which listens to item click, and notifies my activity A and activity A then starts activity B with the data. This looks cleanest for me because A is actually the one the data belongs to, so A starts fragment with data, fragment comes back to A and then A starts B with data. I don't have to pass the data everywhere (serializing!).

But this doesn't seem to work, because how do I pass a listener to the fragment using serialization? What is wrong about using a listener in a fragment to come back to the activity?

How do I solve this?

Edit

I found this solution in a thread about dialog fragments: Get data back from a fragment dialog - best practices?

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

So I add this to my fragment and make my activity implement certain interface.

This would solve the problem of passing data from fragments to B. But I still need to pass the list from A to the fragments. Probably I'll have to use a Parcelable...

Edit: I tried Parcelable, it works. But the objects in my list have a lot of fields between them also maps, for example. Writing the Parcelable code (write / read) is a pain. Maybe I just stick to static data...

Community
  • 1
  • 1
User
  • 31,811
  • 40
  • 131
  • 232
  • I got the same problem. My complex data is calculated and displayed in a list in activity A using a fragment, but details will be displayed in either activity B using a fragment or in two pane layout in the same activity, but different detail fragment. I have absolutely no idea but to parcelable my detail data which is a pain... Did you come up with better ideas? For example put it in application object as an in-between medium? – Zordid Jun 29 '13 at 18:06

1 Answers1

2

The listener solution you found is excellent and actually recommended by Google (http://developer.android.com/training/basics/fragments/communicating.html).

As for your remaining problem of passing data to fragments, are you sure you need the entire list passed, and not just an element of the list for each fragment in the ViewPager? If you just need an element, you can do something like this:

    /*
     * An adapter for swiping through the fragments we have.
     */
    private class CustomPagerAdapter extends FragmentStatePagerAdapter {

        List<Thing> things;

        public CustomPagerAdapter(FragmentManager fragmentManager, List<Thing> things) {
            super(fragmentManager);
            this.things = things;
        }

        @Override
        public int getCount() {
            return things.size();
        }

        @Override
        public Fragment getItem(int position) {
            return ThingFragment.newInstance(things.get(position));
        }
    }

    /*
     * Loads a view based on the thing.
     */
    private static class ThingFragment extends Fragment {

        private String name;

        static ThingFragment newInstance(Thing thing) {

            ThingFragment f = new ThingFragment();

            // add some arguments to our fragment for onCreateView
            Bundle args = new Bundle();
            args.putString("name", thing.getName());
            f.setArguments(args);

            return f;
        }

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            name = getArguments().getString("name");
        }

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

            View v = inflater.inflate(R.layout.thing_fragment_layout, container, false);
                    TextView t = (TextView)v.findViewById(R.id.thingText);
            t.setText(name);
            return v;
        }
    }

And pass your list to the adapter. Of course, this requires that your dynamics be created dynamically, but inflating xml fragments is really easy.

If you actually DO need to pass the entire list, yeah, you might have to make the individual complex object Parcelable, and then add it to your Fragment args like with Bundle.putParcelableArray().

Makario
  • 2,097
  • 21
  • 19
  • Thanks for your answer. In each fragment I'm displaying a grid and I want to pass the list of model items for the grid. I tried out Parcelable but the implementation is awful (my datatype has a lot of fields, also of type map and so on, write the code to serialize and deserialize was a real pain). And it's also very slow, my screen is blank a while. So I guess I'll have to use static data. – User Jun 21 '12 at 11:35
  • Ugh, sorry, the screen is blank because something else, not the parcelable. – User Jun 21 '12 at 12:25
  • 1
    I suppose that, assuming you are certain of your conditions, you could get the information in onAttach through a cast: `@Override public void onAttach(Activity activity) { super.onAttach(activity); mFragmentList = ((MyActivity)activity).getList(); }` – Makario Jun 21 '12 at 15:23