How do I fire an event when click occurs outside a dialog
Asked Answered
T

9

52

I would like to know how to solve a problem I've got.

I have a Dialog which pops up in an activity. The Dialog doesn't cover the whole screen, so the buttons from the activity still show. I can easily close the dialog when there is a touch outside the dialog's bounds with dialog.setCanceledOnTouchOutside(true);

However what I want to do is fire an event if a click is outside the Dialog's bounds (e.g if someone touches a button on the main Activity, it should close the Dialog and fire that event at the same time).

Torritorricelli answered 1/3, 2012 at 12:29 Comment(2)
The normal solution would be to make the dialog modal. Why do you not want to do that?Elwina
Thanks. I'm trying to show a webview(in the dialog) within my main activity so it maintains the application's look.Torritorricelli
R
45

It Works For me,,

        Window window = dialog.getWindow();
        window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

        dialog.show();

See this http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_NOT_TOUCH_MODAL

Replace answered 1/3, 2012 at 12:38 Comment(1)
This kinda works. Only that I can scroll listview but not click it.Ramrod
M
87

When dialog.setCanceledOnTouchOutside(true); then you just override onCancel() like this:

dialog.setOnCancelListener(
        new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                //When you touch outside of dialog bounds, 
                //the dialog gets canceled and this method executes.
            }
        }
);

Type your code inside the onCancel() method so it runs when the dialog gets canceled.

Moonlight answered 25/2, 2015 at 1:46 Comment(4)
best answer.. after 2 years also refer the same answer...not able to upvote again :)Nashua
OVERRIDING Dialog.onCancel(...) works, but dialog.setOnCancelListener(...) didn't work for me.Cacoepy
what do you mean by when? - When dialog.setCanceledOnTouchOutside(true); it's default behavior...Cytoplasm
This should be the accepted answer!! What a great answer.Heritable
R
45

It Works For me,,

        Window window = dialog.getWindow();
        window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

        dialog.show();

See this http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_NOT_TOUCH_MODAL

Replace answered 1/3, 2012 at 12:38 Comment(1)
This kinda works. Only that I can scroll listview but not click it.Ramrod
M
4

In DialogFragment you can use AlertDialog and the answer of @silverTech:

override fun onDismiss(dialog: DialogInterface) {
    yourMethod()
    super.onDismiss(dialog)
}

or

override fun onCancel(dialog: DialogInterface) {
    yourMethod()
    super.onCancel(dialog)
}
Morpheus answered 13/11, 2019 at 9:26 Comment(2)
Why do you recommend inserting logic before super.onDismiss()? And then why is logic after super.onCancel()?Caviness
@AJW, I opened DialogFragment. onCancel is empty, while onDismiss contains some logic about dismissing (closing) the dialog. I advise to write your logic first and then destroying methods (super.onPause(), onDestroy, onSaveInstanceState and so on). It would be better to finalize your variables before the system destroys objects. So, maybe better remove super.onCancel(dialog). I rewrote onCancel after your comment, thanks.Morpheus
S
2

You can use an OnCancelListener to fire an event when a click occurs outside a dialog:

dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
    @Override
    public void onCancel(DialogInterface dialog) {
        yourFunction();
    }
});
Saintsimon answered 6/9, 2018 at 23:19 Comment(1)
How does it differ from @WillNeithan answer (https://mcmap.net/q/342395/-how-do-i-fire-an-event-when-click-occurs-outside-a-dialog)?Morpheus
M
1

If you are within custom dialog class, and wish to catch 'click outside' event - override cancel(). If you wish to catch 'dialog closed' event - override dismiss(). I recommend inserting logic BEFORE super.dismiss(). Kotlin example:

override fun dismiss() {
    Utils.hideKeyboard(mContext, window)
    super.dismiss()
}
Mastic answered 6/5, 2019 at 15:41 Comment(5)
Not a very good answer. Meaning, it is not generic. I have to write new behaviour, rather than noticing old behaviour. In the case where I am making a general dialog utility, There is no way I can know what is behind the dialog.Ramrod
@JaveneCPPMcGowan you can use callbacks from the Custom Dialogs to the views.Mastic
As I said, that is NEW BEHAVIOUR.Ramrod
@Mastic Why do you recommend inserting logic before super.dismiss()?Caviness
the other way around created a situation where only SOMETIMES the hideKeyboard func worked as expected.Mastic
C
1

Its available at com.google.android.material.bottomsheet.BottomSheetDialog

> // We treat the CoordinatorLayout as outside the dialog though it is technically inside
>     coordinator
>         .findViewById(R.id.touch_outside)
>         .setOnClickListener(
>             new View.OnClickListener() {
>               @Override
>               public void onClick(View view) {
>                 if (cancelable && isShowing() && shouldWindowCloseOnTouchOutside()) {
>                   cancel();
>                 }
>               }
>             });

so you can override the ClickListener at your BottomSheetDialog

class MyBottomDialogFragment : BottomSheetDialogFragment(){
 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialog = object : BottomSheetDialog(activity as Context, theme) {
            private var isDialogCancelable: Boolean = false
            private var isDialogCancelableOnTouchOutside: Boolean = false

            override fun onBackPressed() {
                handleBackPressed(this)
            }

            override fun setContentView(layoutResId: Int) {
                super.setContentView(layoutResId)
                setupTouchOutside()
            }

            override fun setContentView(view: View?) {
                super.setContentView(view)
                setupTouchOutside()
            }

            override fun setContentView(view: View?, params: ViewGroup.LayoutParams?) {
                super.setContentView(view, params)
                setupTouchOutside()
            }

            private fun setupTouchOutside() {
                val coordinator = findViewById<View>(com.google.android.material.R.id.coordinator) as CoordinatorLayout
                // We treat the CoordinatorLayout as outside the dialog though it is technically inside
                coordinator
                    .findViewById<View>(com.google.android.material.R.id.touch_outside)
                    .setOnClickListener {
                        if (isDialogCancelable && isShowing && isDialogCancelableOnTouchOutside) {
                            handleTouchOutside(this)
                        }
                    }
            }


            override fun setCancelable(cancelable: Boolean) {
                super.setCancelable(cancelable)
                isDialogCancelable = cancelable
            }

            override fun setCanceledOnTouchOutside(cancel: Boolean) {
                super.setCanceledOnTouchOutside(cancel)
                isDialogCancelableOnTouchOutside = cancel
            }
        }
        dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
        dialog.setCanceledOnTouchOutside(true)
        return dialog
    }

  protected open fun handleBackPressed(dialog: BottomSheetDialog) {
        /* do what you want but after that call dialog.cancel() or dialog.dismiss() */
    }

    protected open fun handleTouchOutside(dialog: BottomSheetDialog) {
        /* do what you want but after that call dialog.cancel() or dialog.dismiss() */        
    }
}
Cassey answered 18/12, 2019 at 12:55 Comment(0)
Q
0

Returning to previous screen with back button command.

Use the override method onCancel in your class that extends DialogFragment . . .

@Override
    public void onCancel(@NonNull DialogInterface dialog)
    {
        super.onCancel(dialog);
        getParentFragment().getActivity().onBackPressed();
    }
Quartz answered 4/12, 2019 at 23:0 Comment(0)
N
0

I found all the other answers quite lengthy and complicated, so I used this approach:

Step 1: Create an ID for the outside container of your element for which you want to generate a click outside event.

In my case, it is a Linear Layout for which I've given id as 'outsideContainer'

Step 2: Set an onTouchListener for that outside container which will simply act as a click outside event for your inner elements!

outsideContainer.setOnTouchListener(new View.OnTouchListener() {
                                            @Override
                                            public boolean onTouch(View v, MotionEvent event) {
                                                // perform your intended action for click outside here
                                                Toast.makeText(YourActivity.this, "Clicked outside!", Toast.LENGTH_SHORT).show();
                                                return false;
                                            }
                                        }
    );
Nimiety answered 21/5, 2021 at 7:35 Comment(0)
O
0

@WillNeithan answer in Kotlin:

val alertDialog = AlertDialog.Builder(this).create()
// ... complete setting up alertDialog
    
alertDialog.setOnCancelListener(
    DialogInterface.OnCancelListener {
        println("User Tapped Outside to Dismiss alertDialog")                 
    }
)
Overload answered 11/6, 2023 at 6:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.