9

I have run into a bit of a roadblock. I have a scenario VERY similar to the one described at: DialogFragment - retaining listener after screen rotation

The proposed solution works fine for the author because his dialog is being called from an activity. My case is the exact same, but my Custom Dialog is called from a Fragment instead of an Activity. (IE Activity->Fragment->Dialog)

I implemented the same solution (setting the listener in onResume from the calling Fragment) but it doesn't work in this case.

What seems to be happening is that when the screen is rotated, Android kills the Dialog and Fragment. Then recreates them IN THAT ORDER. So when my onCreateDialog is called on my custom dialog the containing Fragment is yet to be recreated, so it has null for the listener to set for positive and negative buttons.

Does anyone know a way around this?

public class RecipeDetailEditFragment extends SherlockFragment implements DialogInterface.OnClickListener {
    private EditStepFragmentDialog stepDialog;
    private Recipe newRecipe; //main data object implements parcelable
    ...
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        stepDialog = EditStepFragmentDialog.newInstance(newRecipe);
        //I've also tried passing 'this' into the newInstance constructor and 
        //setting the listener there, but that doesn't work either
    }

    public void onResume() {
        stepDialog.setListener(this);
        super.onResume();
    }
    ...
}


public class EditStepFragmentDialog extends DialogFragment {
    private DialogInterface.OnClickListener ocl;
    private static final String ARG_RECIPE = "recipe";
    private Recipe recipe;

    public EditStepFragmentDialog() {}

    public static EditStepFragmentDialog newInstance(Recipe rec) { //(Recipe rec, DialogInterface.OnClickListener oc) as mentioned doesn't work.
        EditStepFragmentDialog dia = new EditStepFragmentDialog();
        Bundle args = new Bundle();
        args.putParcelable(ARG_RECIPE, rec);

        //dia.setListener(oc);
        return dia;
    }

    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder adb = new AlertDialog.Builder(getActivity());

        if (getArguments().containsKey(ARG_RECIPE)) {
            recipe = (Recipe) getArguments().getParcelable(ARG_RECIPE);
        }
        ...

        adb.setPositiveButton("Done", ocl);
        adb.setNegativeButton("Cancel", ocl);

        ...

        return adb.create();
    }

    public void setListener(DialogInterface.OnClickListener cl) {
        ocl = cl;
    }
}
htafoya
  • 18,261
  • 11
  • 80
  • 104
user2229491
  • 321
  • 1
  • 4
  • 10

2 Answers2

4

I ran through all of the options on the discussed links and none of the solutions ended up working for me. I also tried a number of additional options after further googling like get/setTargetFragment, and FragmentManager.put/getFragment. These didn't work for me either. Then I took another look at:

http://developer.android.com/training/basics/fragments/communicating.html

Where they specifically say "Two Fragments should never communicate directly". I think this is one of the cases where that's really proven true.

I ended up implementing the suggested callback mechanism provided there and ended up with this:

In DialogFragment:

public interface OnEditStepDialogListener {
    public void onEditStepDialogPositive(int pos, String description);
}

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

    try {
        mCallback = (OnEditStepDialogListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement OnEditStepDialogListener");
    }
}

In hosting Activity:

public class MyActivity extends SherlockFragmentActivity implements EditStepFragmentDialog.OnEditStepDialogListener {

...

@Override
public void onEditStepDialogPositive(int pos, String desc) {
    FragmentManager fm = getSupportFragmentManager();
    RecipeDetailEditFragment ef = (RecipeDetailEditFragment)fm.findFragmentByTag(RecipeDetailEditFragment.TAG);

    ef.applyStepEdit(pos, desc);
}

In Fragment that fires off the FragmentDialog:

public static final String TAG = "tag1";

public void applyStepEdit(int pos, String description) {
    ...
}

This works perfectly, if opened then orientation change and edit is completed, it actually triggers the ultimate function I need run in the calling Fragment instead of either crashing or not doing anything (null listener).

user2229491
  • 321
  • 1
  • 4
  • 10
  • Great you found a solution! Just a small comment, this makes you able to extend the fragment to take yet another argument on creation, an OnEditStepDialogListener to handle the callback, just in case you do not like handling every callback in you Activity class. – cYrixmorten Sep 27 '13 at 10:45
0

Would it be a disaster to report back to your activity and make that create the dialog?

I just reviewed my code to see what I am doing as I have not encountered this problem. My take on it is something like this:

- MyActivity
     |
     ---- MapsFragmet (for example)
     |
     ---- DirectionsModule (simple class that is handed Context)
     |
     ---- PointsOfInterestModule (simple class that is handed Context)

Thus with this construction the activity uses the fragment purely to show the map but can use it for both the purpos of directions, or points of interest, depending on which Module is invoked.

Now when the module encounter a problem, or needs userinteraction, it reports back to MyActivity, which then for instance displays a DialogFragment.

Would like to give a better answer, as I dont see why you should not be able to invoke a DialogFragment from within another Fragment and expect a nice behaviour.

Just in case, have you set setRetainInstance(true) on you Fragment?

Edit:

Ok, so I just reviewed your newly commited code, here is my new idea:

Extend the arguments of your dialog to take a context so u can call it like this:

stepDialog = EditStepFragmentDialog.newInstance(getActivity(), newRecipe);

Next use the the added context instead of getActivity() in your Dialog:

AlertDialog.Builder adb = new AlertDialog.Builder(context);

I suspect (not sure about this) that SherlockFragment counts as an instance of Activity, so when you call getActivity() in your dialog, it is tied to your fragment.

cYrixmorten
  • 7,110
  • 3
  • 25
  • 33
  • It wouldn't be a disaster, but I'd prefer a solution that allows a Fragment to respond to a dialog it triggers its self. If no one else has a better option I'll consider this the best answer. I have tried setRetainInstance on the fragment, and it didn't seem to help. – user2229491 Sep 26 '13 at 12:44
  • just made a new edit (do you get automatically informed about this?) – cYrixmorten Sep 26 '13 at 13:11
  • I just tried doing it that way (internal context, and setContext function called in getInstance constructor to set it) and now it crashes if you rotate after popup is shown. I'm pretty sure it's the same reason as the issue with Listener...the Dialog is getting recreated before the Fragment so, when it first runs onCreateDialog the internal context object hasn't been set yet... – user2229491 Sep 26 '13 at 15:40
  • Ok, now I think I found a solution: http://stackoverflow.com/questions/8235080/fragments-dialogfragment-and-screen-rotation You need to scroll down a bit to get to the answer. – cYrixmorten Sep 26 '13 at 15:53
  • Who was the poster of the answer you are refering to? I looked at that post before asking this quesiton, and from what I can tell most of the answers focus on how to get your retained instance bundle to work properly. Listeners aren't parcelable, so that doesn't factor into my scenario. – user2229491 Sep 26 '13 at 16:17
  • Well, just thought there was a lot of suggestions to a solution, have you tried em all? – cYrixmorten Sep 26 '13 at 16:30
  • I am thinking setting setRetainInstance(true) on the dialog and adding the onDestroyView() they mention. If I were you I would backtrace the problem by adding a completely stripped DialogFragment and see if it still crashes. I believe the problem lies in the Dialog and/or the instantion of it. And not that it is created from within a fragment, besides maybe the fact that you use SherlockFragment, which is also part of the discussion on the linked site. – cYrixmorten Sep 26 '13 at 17:41