Allow outside touch for DialogFragment
Asked Answered
B

9

18

I have a Fragment in my app that shows a DialogFragment.
I have in the fragment a button that closes the dialog. But when I show the dialogFragment, the touches outside from the dialog do nothing and I can't click the buttons outside from the dialog fragment.

How can I allow outside touches for DialogFragment?

Bellaude answered 13/3, 2013 at 10:21 Comment(0)
B
27

In order to do that, a flag of the Window that allows the outside touch should be turned on and for the good appearance the background dim flag should be cleared.
Since it must be done after dialog is created, I've implemented it via the Handler.

@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    // This is done in a post() since the dialog must be drawn before locating.
    getView().post(new Runnable() {

        @Override
        public void run() {

            Window dialogWindow = getDialog().getWindow();

            // Make the dialog possible to be outside touch
            dialogWindow.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
            dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

            getView().invalidate();
        }
    });
}

At this moment the outside touch is possible.

In case we want to make it nicer and without the frame, the following code can be added:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Hide title of the dialog
    setStyle(STYLE_NO_FRAME, 0);
}
Bellaude answered 14/3, 2013 at 14:18 Comment(3)
Do you have any idea how to achieve the same for BottomSheetDialogFragment?Epictetus
@Yanix This is not working when DialogFragment is set to full screen window.setLayout(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT)Stapes
This works in case of Android 6, 7, 7.1, 8, but fails in case of Android 9. No clicks outside of the DialogFragment get though on Android 9.Meghanmeghann
W
7

This is more generic flag to allow any action not only touch actions

window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
Waterer answered 20/12, 2014 at 12:26 Comment(0)
S
1

I have found alternative way to handle dismiss on touch outside. Please check below code sample, that would definitely work,

@Override
public void onStart() {
    // mDialogView is member variable
    mDialogView = getView();
    mDialogView.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            float eventX = event.getRawX();
            float eventY = event.getRawY();

            int location[] = new int[2];
            mDialogView.getLocationOnScreen(location);

            if (eventX < location[0] || eventX > (location[0] + mDialogView.getWidth()) || eventY < location[1]
                    || eventY > location[1] + mDialogView.getHeight()) {
                dismiss();
                return true;
            }
            return false;
        }
    });
}

In my case, I was getting getDialog() as not null value, but getDialog().dismiss() was not working. Also, the solution that is marked as right above, also did not work in my case. So, I took this approach.

Sectarianize answered 20/1, 2015 at 10:28 Comment(0)
T
1

@yaniv has provided the answer. But in case you want, here's a snippet where you get the same result but withou the need to post a Runnable to the root View:

@NonNull
@Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
    final Dialog dialog = super.onCreateDialog(savedInstanceState);
    dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
    return dialog;
}
Timmons answered 18/7, 2019 at 12:4 Comment(0)
M
1

I have tested the accepted (Yaniv's) answer on

Galaxy Tab A 9.7 Android 7.1.1: ok
Galaxy Tab A 8.0 Android 7    : ok
Galaxy Tab S4 Android 9       : no touch outside DialogFragment
Galaxy S5 Android 6.0.1       : ok
Galaxy Tab S2 Android 7       : ok
Galaxy Tab S3 Android 9       : no touch outside DialogFragment
Galaxy Tab S4 Android 9       : no touch outside DialogFragment
Galaxy Note Edge Android 6.0.1: ok
Galaxy Note4 Android 6.0.1    : ok

and it really looks like it does not work on Android 9. When I say 'does not work' I mean when I try clicking on a Button that's outside the DialogFragment, its onClick() method does not get called at all.

EDIT: in light of my findings (in comments) I think the below should be the accepted answer. The below works everywhere I have tested, including the emulators, Google Pixel 6, LG Nexus 5X, HTC Desire 12, about 5 Huawei phones, and about 20 Samsung phones running everything from Android 5 to 10.

public Dialog onCreateDialog(Bundle savedInstanceState)
    {
    FragmentActivity act = getActivity();
    AlertDialog.Builder builder = new AlertDialog.Builder(act);

    // ... do your stuff here....

    Dialog dialog = builder.create();

    dialog.setCanceledOnTouchOutside(false);

    Window window = dialog.getWindow();

    if( window!=null )
      {
      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                      WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
      }

    return dialog;
    }
Meghanmeghann answered 13/4, 2020 at 11:58 Comment(4)
It does not work on Android 10 and the upcoming Android 11. So summarizing: works only on Android 6 , 7 , 7.1 , 8.0 , 8.1 and does not work on Android 9, 10 and 11.Meghanmeghann
The above results were observed on real Samsung phones. Curiously, when running on the emulator with Android 9, 10 or 11 - the accepted answer does work.Meghanmeghann
Even better - now I've tested on a Huawei Mate Pro running Android 9, and over there it's working. So it is looking like this is not working only on Samsung phones running Android 9 or 10 or 11.Meghanmeghann
ok I finally found a way to make the accepted answer work also on the Samsung phones: move the section that sets the 'FLAG_NOT_TOUCH_MODAL' flag to the end of onCreateDialog(), and do not post() it - just call it from there. Looks like on Samsung phones, this flag needs to be set as early as possible to work.Meghanmeghann
M
1

In light of my little investigation above, I really think this should be the accepted answer:

  @Override
  public Dialog onCreateDialog(Bundle savedInstanceState)
    {
    FragmentActivity act = getActivity();
    AlertDialog.Builder builder = new AlertDialog.Builder(act);

    // ... do your stuff here

    Dialog dialog = builder.create();

    dialog.setCanceledOnTouchOutside(false);

    Window window = dialog.getWindow();

    if( window!=null )
      {
      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                      WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
      }

    return dialog;
    }

i.e. move the setting of flags to the end of onCreateDialog(), and add setCancelOnTouchOutside(false).

The accepted answer does not work on Samsung phones running Android 9 and 10 ( and most probably 11). Looks like on those systems, samsung has changed something that makes setting those flags only work if they are set very early.

The above works everywhere, including Google emulators, LG Nexus 5X, HTC Desire 12, Google Pixel 6, a few Huawei phones, and all (about 20) Samsung phones I've tested this on.

Meghanmeghann answered 14/4, 2020 at 22:15 Comment(0)
O
0

You need to set a proper style for your needs. You can read more about styles for FragmentDialogs in the official docs.

I believe you could use following style:

  • STYLE_NO_FRAME
Occam answered 13/3, 2013 at 11:51 Comment(1)
I'm already working with STYLE_NO_FRAME, and it is the only one that OK for me (since the UI).Bellaude
T
0

Well, let's be specific. If you want to work with outside views then you should not use dialog fragment, use fragment instead like this.

 FragmentTransaction transaction = fragmentManager.beginTransaction();
    // For a little polish, specify a transition animation
    transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    // To make it fullscreen, use the 'content' root view as the container
    // for the fragment, which is always the root view for the activity
    transaction.add(android.R.id.content, newFragment)
               .addToBackStack(null).commit();

And make the layout wrap_content, which will apparently looks like Dialog and by default it is placed at the top of the screen.

For more details please visit here

Thallophyte answered 4/3, 2016 at 11:36 Comment(0)
F
0

@Alexandre Bodi answer should be accepted.

In my case Runnable() made whole screen blink each time I opened fragment. If anyone needs clean Kotlin approach without blinking, here you go:

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog = super.onCreateDialog(savedInstanceState)
    dialog.window?.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
    dialog.window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
    return dialog
}
Fiddling answered 19/9, 2022 at 13:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.