76

I am using a DialogFragment, which I am showing like this from an Activity:

DialogFragmentImage dialog = DialogFragmentImage.newInstance(createBitmap());
dialog.onDismiss(dialog);.onDismiss(this);          
dialog.show(getFragmentManager(), "DialogFragmentImage");

I would like to check when the DialogFragment was dismissed (for example when the back button was pressed), but in my Activity. How can I do that? How can I "tell" my activity that the DialogFragment has been dismissed?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
deimos1988
  • 5,896
  • 7
  • 41
  • 57

11 Answers11

116

Make your Activity implement OnDismissListener

public final class YourActivity extends Activity implements DialogInterface.OnDismissListener {

    @Override
    public void onDismiss(final DialogInterface dialog) {
        //Fragment dialog had been dismissed
    }

}

DialogFragment already implements OnDismissListener, just override the method and call the Activity.

public final class DialogFragmentImage extends DialogFragment {

    ///blah blah

    @Override
    public void onDismiss(final DialogInterface dialog) {
        super.onDismiss(dialog);
        final Activity activity = getActivity();
        if (activity instanceof DialogInterface.OnDismissListener) {
            ((DialogInterface.OnDismissListener) activity).onDismiss(dialog);
        }
    }

}

If you're starting the dialog from a fragment using the childFragment manager (API>=17), you can use getParentFragment to talk to the onDismissListener on the parent fragment.:

public final class DialogFragmentImage extends DialogFragment {

    ///blah blah

    @Override
    public void onDismiss(final DialogInterface dialog) {
        super.onDismiss(dialog);
        Fragment parentFragment = getParentFragment();
        if (parentFragment instanceof DialogInterface.OnDismissListener) {
            ((DialogInterface.OnDismissListener) parentFragment).onDismiss(dialog);
        } 
    }

}
Aphex
  • 7,390
  • 5
  • 33
  • 54
Yaroslav Mytkalyk
  • 16,950
  • 10
  • 72
  • 99
44

Here is my answer. It's a bit late but it's maybe benefit someone passing by.

FragmentManager fm = getFragmentManager();

YourDialogFragment dialog = new YourDialogFragment();
dialog.show(fm,"MyDialog");

fm.executePendingTransactions();
dialog.getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialogInterface) {
                       //do whatever you want when dialog is dismissed
                    }
                });

We need to call

fm.executePendingTransactions(); 

To make sure that FragmentTransaction work has been performed. Otherwise NullPointerException can occur when calling setOnDismissListener().

Sorry if there is any mistake. Hope this help.

Boonya Kitpitak
  • 3,607
  • 1
  • 30
  • 30
  • @Simas: Can you elaborate on "doesn't work"? Is there a compile error? a runtime problem? – LarsH Aug 16 '17 at 09:43
  • @LarsH as far as I remember calling `fm.executePendingTransactions();` didn't protect from an NPE as is said on this answer. – Simas Aug 16 '17 at 10:29
  • 4
    Seems clean solution, but on screen rotation, it will call `onDismiss` callback, which may trigger unwanted logic, assuming that this logic should be run when user makes some actions in the dialog after which `dismiss()` is called – Max Ch Dec 31 '18 at 06:02
  • Works for me perfectly. – Sam Chen Sep 04 '19 at 20:01
  • don't forget to call dialog.dismiss() inside onDismiss(DialogInterface dialogInterface) because dialog will reappear when you switch apps and come back... – user1159819 May 04 '20 at 02:02
  • 2
    It's noted on official documentation that instead of calling `show()` then `executePendingTransactions()`, it would be more concise to just call idiomatic `showNow()`. – Hiro Nov 20 '20 at 00:10
  • and dismiss listener set like this on the dialog doesn't seem to be called when the dialogfragment itself is `dismiss()`ed, which is the recommended thing to do. – Hiro Nov 20 '20 at 01:25
15

This is an old issue but I found no solution I am happy with. I don't like passing any Listeners to my DialogFragment or set a TargetFragment, because that may break on orientation change. What do you think about this?

        MyDialog d = new MyDialog();
        d.show(fragmentManager, "tag");
        fragmentManager.registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {
            @Override
            public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
                super.onFragmentViewDestroyed(fm, f);
                //do sth      
        fragmentManager.unregisterFragmentLifecycleCallbacks(this);
                }
            }, false);
Kaskasi
  • 1,330
  • 1
  • 14
  • 15
8

Alternative answer, if you don't have access to the methode onDismiss of activity.

//DIALOGFRAGMENT
//Create interface in your DialogFragment (or a new file)
public interface OnDismissListener {
   void onDismiss(MyDialogFragment myDialogFragment);
}
//create Pointer and setter to it
private OnDismissListener onDismissListener;
public void setDissmissListener(DissmissListener dissmissListener) {
   this.dissmissListener = dissmissListener;
}
//Call it on the dialogFragment onDissmiss
@Override
public void onDismiss(DialogInterface dialog) {
   super.onDismiss(dialog);

   if (onDismissListener != null) {
      onDismissListener.onDismiss(this);
   }
}

//OTHER CLASS, start fragment where you want
MyDialogFragment df = new MyDialogFragment();
df.setOnDismissListener(new MyDialogFragment.OnDismissListener() {
   @Override
   public void onDismiss(MyDialogFragment myDialogFragment) {
      //Call when MyDialogFragment close
   }
});
df.show(activity.getFragmentManager(), "myDialogFragment");

edit : if system need to recreate DialogFragment: you can find it with

MyDialogFragment myDialogFragment = getFragmentManager().findFragmentByTag("MyDialogFragment"); 
if(myDialogFragment != null) { 
   myDialogFragment.setOnDismissListener(...); 
}
Anthone
  • 2,156
  • 21
  • 26
  • I was doing this and thought I was clever, but this is dangerous. If the framework decides to recreate the dialog (due to a configuration change or screen rotation), it will use the empty dialog fragment constructor and the onDismissListener will not be set. – Aphex Nov 26 '15 at 16:03
  • Yes you're right. Add this in onCreate of your activity MyDialogFragment myDialogFragment = getFragmentManager().findFragmentByTag("MyDialogFragment"); if(myDialogFragment != null) { myDialogFragment.setOnDismissListener(...); } – Anthone Nov 27 '15 at 10:10
  • And of course add the same tag in your dialogFragment.show(getFragmentManager(), "MyDialogFragment") – Anthone Nov 27 '15 at 10:12
4
public class OpcoesProdutoDialogo extends DialogFragment{
    private DialogInterface.OnDismissListener onDismissOuvinte;
.
.
.

@Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        if(onDismissOuvinte!=null)
            onDismissOuvinte.onDismiss(dialog);
    }

    public void setOnDismissListener(@Nullable DialogInterface.OnDismissListener listener) {
        this.onDismissOuvinte = listener;
    }
}

and in call

OpcoesProdutoDialogo opcProduto = OpcoesProdutoDialogo.criar(itemPedido);
        opcProduto.show(getFragmentManager(), "opc_produto_editar");
        opcProduto.setOnDismissListener(d->{
            adapterItens.notifyItemChanged(posicao);
        });
d3roch4
  • 361
  • 2
  • 5
2

Solution using kotlin and additional interface. (an example for a fragment will be shown here, but with a few changes it will work in an activity as well)

First you need to create an interface (the set of parameters can be any):

interface DialogCloseListener {
    fun handleDialogClose(dialog: DialogInterface)
}

Then implement this interface in the fragment that calls the DailogFragment:

class YourParentFragment: Fragment(), DialogCloseListener {
override fun handleDialogClose(dialog: DialogInterface) {
// do something
}
}

Now go to your DialogFragment. Implement the onDismiss method. In it, check if the parent fragment implements your interface, call your method, passing the necessary parameters there:

    override fun onDismiss(dialog: DialogInterface) {
        super.onDismiss(dialog)
        if(parentFragment is DialogCloseListener){
            (parentFragment as DialogCloseListener).handleDialogClose(dialog)
        }
    }

I think that this way is good because you can track a specific close event (by passing a certain parameter to the method), for example, canceling an order, and somehow handle it.

Lnight
  • 31
  • 6
1

You can subclass DialogFragment and provide your own listener that is going to be called and in onCancel.

var onDismissListener: (() -> Unit)? = null

For the ones not familiar with Kotlin this is just an anonymous interface that saves boilerplate iterface in Java. Use a field and a setter in Java.

And then in onCancel

    override fun onCancel(dialog: DialogInterface?) {
    super.onCancel(dialog)
    onDismissListener?.invoke()
}

Have fun!

1

If you don't like the solution of @yaroslav-mytkalyk, in which the fragment needs to cast the activity / parent fragment, here's another one:

Here's the idea:

  1. Expose a listener in your fragment, DialogFragmentImage.
  2. Implement the listener in your activity and pass it to the fragment when creating it. Make sure to use a tag as well in order to be able to find the fragment later (read below).
  3. In onStop(), remove the listener in order not to leak the activity if it's destroyed. This will happen when the screen is rotated, as the activity will be re-created.
  4. In onResume(), check if the fragment exists and if yes, re-add the listener.

Expose a listener from your fragment:

class MyFragment extends DialogFragment {

    public interface OnDismissListener {
        void dismissed();
    }

    @Nullable
    private OnDismissListener onDismissListener;

    public void setOnDismissListener(@Nullable OnDismissListener onDismissListener) {
        this.onDismissListener = onDismissListener;
    }

    /*
    If you are calling dismiss() or dismissAllowingStateLoss() manually,
    don't forget to call:
    if (onDismissListener != null) {
        onDismissListener.dismissed();
    }

    Otherwise, override them and call it there.
    */
}

And this is how your activity should look like:

class MyActivity extends AppCompatActivity {

    private static final String MY_FRAGMENT_TAG = "my_fragment";

    private MyFragment.OnDismissListener myFragmentListener = () -> {

        // ...
    };

    /**
     * Shows the fragment. Note that:
     * 1. We pass a tag to `show()`.
     * 2. We set the listener on the fragment.
     */
    private void showFragment() {

        MyFragment fragment = new MyFragment();
        fragment.show(getSupportFragmentManager(), MY_FRAGMENT_TAG);
        fragment.setOnDismissListener(myFragmentListener);
    }

    @Override
    protected void onStart() {

        super.onStart();

        // Restore the listener that we may have removed in `onStop()`.
        @Nullable MyFragment myFragment =  (MyFragment) getSupportFragmentManager().findFragmentByTag(MY_FRAGMENT_TAG);
        if (myFragment != null) {
            myFragment.setOnDismissListener(myFragmentListener);
        }
    }

    @Override
    protected void onStop() {

        // If the fragment is currently shown, remove the listener so that the activity is not leaked when e.g. the screen is rotated and it's re-created.
        @Nullable MyFragment myFragment =  (MyFragment) getSupportFragmentManager().findFragmentByTag(MY_FRAGMENT_TAG);
        if (myFragment != null) {
            myFragment.setOnDismissListener(null);
        }

        super.onStop();
    }
}
Minas Mina
  • 2,058
  • 3
  • 21
  • 35
1

Care : all example aren't correct because your fragment should have a no-arg constructor !

Working code with back gesture and close button in the fragment itself. I removed useless code stuff like getting arg in onCreate etc.

Important : onDismiss is also call when orientation change so as a result you should check if the context is not null in your callback (or using other stuff).

public class MyDialogFragment extends DialogFragment {
    
    public static String TAG = "MyFragment";

    public interface ConfirmDialogCompliant {
        void doOkConfirmClick();
    }

    
    public MyFragment(){
        super();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View rootView = inflater.inflate(R.layout.fragment_layout, container, false);

        ((ImageButton) rootView.findViewById(R.id.btn_close)).setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // close fragment
                dismiss();
            }
        });
        return rootView;
    }

    @Override
    public void onDismiss(@NonNull DialogInterface dialog) {
        super.onDismiss(dialog);
        // notify
        if(caller != null)
           caller.doOkConfirmClick();
        }
    }

    public void setCallback(ConfirmDialogCompliant caller) {
        this.caller = caller;
    }

    public static MyDialogFragment newInstance(String id) {
        MyDialogFragment f = new MyDialogFragment();

        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putString("YOU_KEY", id);
        f.setArguments(args);

        return f;
    }
}

And now how to call it from parent.

MyDialogFragment.ConfirmDialogCompliant callback = new MyDialogFragment.ConfirmDialogCompliant() {

            @Override
            public void doOkConfirmClick() {
                // context can be null, avoid NPE
                if(getContext() != null){

                }

            }

        };

    MyDialogFragment fragment = MyDialogFragment.newInstance("item");
    fragment.setCallback(callback);
    fragment.show(ft, MyDialogFragment.TAG);
        new MyDialogFragment(callback, item);

    fragment.show(getActivity().getSupportFragmentManager(), MyDialogFragment.TAG);
   

Additionnal source : https://developer.android.com/reference/android/app/DialogFragment

Zain
  • 37,492
  • 7
  • 60
  • 84
Zhar
  • 3,330
  • 2
  • 24
  • 25
1

Kotlin Answer

private fun showMyCustomDialog() {

    // Show.
    MyCustomDialogFragment().show(fm, "MyCustomDialogFragment")
    
    // Set pending transactions.
    fm.executePendingTransactions()
    
    // Listen dialog closing.
    MyCustomDialogFragment().dialog?.setOnDismissListener { 
        
        // You can do you job when it closed.
    }
}
canerkaseler
  • 6,204
  • 45
  • 38
0

Try this

dialog.setOnDismissListener {
            Log.e("example","example")
        }
       

Have Fun!