5

I try to implement a callback from a DialogFragment. There is a good example, but they don't open this DialogFragment from a Fragment. http://developer.android.com/guide/topics/ui/dialogs.html#PassingEvents

So here is my code:

public class EditDateDialogFragment extends DialogFragment {
    // Use this instance of the interface to deliver action events
    EditDateDialogListener mListener;

    /* The activity that creates an instance of this dialog fragment must
     * implement this interface in order to receive event callbacks.
     * Each method passes the DialogFragment in case the host needs to query it. */
    public interface EditDateDialogListener {
        public void onDialogPositiveClick(DialogFragment dialog);
        public void onDialogNegativeClick(DialogFragment dialog);
    }


    public static EditDateDialogFragment newInstance( int currentCategoryId ) {
        EditDateDialogFragment p = new EditDateDialogFragment();
        Bundle args = new Bundle();
        args.putInt("currentRecordId", currentCategoryId);
        p.setArguments(args);
        return p;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        mCurrentRecordId = getArguments().getInt("currentRecordId");
        super.onCreate(savedInstanceState);
    }

    public void onAttach(SherlockActivity activity) {

        super.onAttach(activity);

        try {
            // Instantiate the EditDateDialogListener so we can send events to the host
            mListener = (EditDateDialogListener) activity;
        } catch (ClassCastException e) {
            // The activity doesn't implement the interface, throw exception
            throw new ClassCastException(activity.toString() + " must implement EditDateDialogListener");
        }

    }

        @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        LayoutInflater inflater = LayoutInflater.from(getActivity());
        final View v = inflater.inflate(R.layout.fragment_dialog_edit_date, null);

        return new AlertDialog.Builder(getActivity()).setTitle("Set Date...").setView(v).setCancelable(true).setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.d("", "Dialog confirmed");

                mListener.onDialogPositiveClick(EditDateDialogFragment.this);

            }
        }).setNegativeButton("Abort", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.d("", "Dialog abort");
                dialog.cancel();
            }
        }).create();
    }
}

In RecordDetailFragment.java i implement the interface and create a new instance of the EditDateDialogFragment at this way (just the important parts):

public class RecordDetailFragment extends SherlockFragment implements EditDateDialogFragment.EditDateDialogListener {
...
 DialogFragment editDateFragment = EditDateDialogFragment.newInstance( recordId );
             editDateFragment.show(getActivity().getSupportFragmentManager(), "EditDateDialogFrame");
@Override
    public void onDialogPositiveClick(DialogFragment dialog) {
        LOGD(TAG, "Overriden Dialog confirmed");
        //((EditDateDialogFragment) dialog).mDatePicker;

    }

    @Override
    public void onDialogNegativeClick(DialogFragment dialog) {
        // TODO Auto-generated method stub

    }
...
}

Now the public void onAttach(SherlockActivity activity) in the EditDateDialogFragment is never called, because I create the DialogFragment from a Fragment instead of an Activity? How to fix this?

UPDATE: In the RecordDetailFragment I insert this into the onCreate()

if (savedInstanceState != null) {
    EditDateDialogFragment dpf = (EditDateDialogFragment) getActivity().getSupportFragmentManager().findFragmentByTag("EditDateDialogFragment");
    if (dpf != null) {
        dpf.setListener((EditDateDialogListener) this);
    }
}

I changed the instantiation of the DialogFragment to

 EditDateDialogFragment editDateFragment = EditDateDialogFragment.newInstance( recordId );
             editDateFragment.setListener((EditDateDialogListener) this);
             editDateFragment.show(getActivity().getSupportFragmentManager(), "EditDateDialogFragment");

Note the EditDateDialogFragment instead of DialogFragment. I'm not sure how to update the reference in the dialog.

No3x
  • 470
  • 1
  • 8
  • 28

4 Answers4

10

Just jumped into the same problem, the solution was very simple. Instead of overriding

public void onAttach(Context context) {}

override this:

public void onAttach(Activity activity) {}

Everything is now fine with DialogFragment.

Torsten Scholz
  • 856
  • 1
  • 9
  • 22
Xavier Lin
  • 328
  • 2
  • 7
9

How to fix this?

I'm guessing that you want the RecordDetailFragment instance to behave as the EditDateDialogListener for the DialogFragment. If yes then you need to explicitly set it(and update it) as the listener:

DialogFragment editDateFragment = EditDateDialogFragment.newInstance( recordId );
editDataFragment.setListener(RecordDetailFragment.this);
editDateFragment.show(getActivity().getSupportFragmentManager(), "EditDateDialogFrame");

Where setListener() is a method in the EditDialogFragment like this:

public void setListener(EditDateDialogListener listener) {
     mListener = listener;
}

As the user rotates the phone, for example, the Activity along with its fragments will be recreated and you need to re set the listener to point to the newly created RecordDetailFragment instance(you may want to use a WeakReference for mListener). Something similar you can find in this answer(you'll look for the two fragments in the onCreate).

Edit: In the onCreate method of the Activity:

if (savedInstanceState != null) {
    RecordDetailFragment df = (RecordDetailFragment) getSupportFragmentManager().findFragmentByTag("rdf"); // "rdf" is the tag used when you add the RecordDetailFragment to the activity
    EditDateDialogFragment s = (EditDateDialogFragment) getSupportFragmentManager().findFragmentByTag("tag"); // "tag" is the string set as the tag for the dialog when you show it
    if (s != null) {
                   // the dialog exists so update its listener
        s.setListener(df);
    }
}
Community
  • 1
  • 1
user
  • 86,916
  • 18
  • 197
  • 190
  • I don't get the Listener update working. The desciption where I have to update the listener reference in your comment are a bit unprecise to me. – No3x Dec 28 '12 at 15:10
  • @No3x Can you share some information/code on how you did the listener update? – user Dec 28 '12 at 15:12
  • I'm confused.. In which activity? I want to communicate between the Fragment and the DialogFragment. – No3x Dec 28 '12 at 16:06
  • @No3x And where do you use those two fragments? Aren't they placed in a `FragmentActivity` / `Activity`? – user Dec 28 '12 at 16:08
3

Somewhere in the onCreateDialog cast the mListener to the getActivity():

try {
    mListener = (EditDateDialogListener) getActivity();
} catch (Exception e) {
    throw new ClassCastException(getActivity().toString()
            + " must implement EditDateDialogListener");
}
Manin
  • 29
  • 1
  • 3
0

A more "modern" approach is to use the new Fragment Result API.

Add a result listener on Fragment A (parent) onCreate:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    childFragmentManager.setFragmentResultListener("requestKey", this) { key, bundle ->
        val result = bundle.getString("bundleKey")
    }

}

Wherever you need, set result on child Fragment B (on a button click listener, for instance):

button.setOnClickListener {
    val result = "resultSample"

    setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}

More info on the docs: https://developer.android.com/guide/fragments/communicate#kotlin

Geraldo Neto
  • 3,670
  • 1
  • 30
  • 33