Reputation: 3391
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?
Upvotes: 18
Views: 15166
Reputation: 416
@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
}
Upvotes: 0
Reputation: 1231
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;
}
Upvotes: 1
Reputation: 1231
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.
Upvotes: 1
Reputation: 454
@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;
}
Upvotes: 1
Reputation: 523
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
Upvotes: 0
Reputation: 572
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.
Upvotes: 1
Reputation: 4220
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);
Upvotes: 8
Reputation: 3391
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);
}
Upvotes: 27
Reputation: 28162
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:
Upvotes: 0