How to cancel an Dialog themed like Activity when touched outside the window?

AndroidDialogAndroid ActivityTouch

Android Problem Overview


I have an activity with a Dialog theme and I would like to close (finish) this activity when someone touches the screen anywhere outside this activity's window ? How can I do this ?

Android Solutions


Solution 1 - Android

Just to point out that there is a way to get dialog-like "touch outside to cancel" behaviour from an Activity themed as a dialog, though I've not fully investigated whether it has unwanted side effects.

Within your Activity's onCreate() method, before creating the view, you're going to set two flags on the window: One to make it 'non-modal', to allow views other than your activity's views to receive events. The second is to receive notification that one of those events has taken place, which will send you an ACTION_OUTSDIE move event.

If you set the theme on the activity to the dialog theme, you'll get the behaviour you want.

It looks something like this:

public class MyActivity extends Activity {

 @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    // Make us non-modal, so that others can receive touch events.
    getWindow().setFlags(LayoutParams.FLAG_NOT_TOUCH_MODAL, LayoutParams.FLAG_NOT_TOUCH_MODAL);

    // ...but notify us that it happened.
    getWindow().setFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
    
    // Note that flag changes must happen *before* the content view is set.
    setContentView(R.layout.my_dialog_view);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    // If we've received a touch notification that the user has touched
    // outside the app, finish the activity.
    if (MotionEvent.ACTION_OUTSIDE == event.getAction()) {
      finish();
      return true;
    }

    // Delegate everything else to Activity.
    return super.onTouchEvent(event);
  }
}

Solution 2 - Android

I found an even simpler answer that has worked perfectly for me. If you're using an activity with the dialog theme then you can apply this.setFinishOnTouchOutside(true); to the activity's onCreate() method.

@Override
protected void onCreate(Bundle savedInstanceState) 
{
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_yoptions);
	this.setFinishOnTouchOutside(true);
}

Solution 3 - Android

It's very simple, just set the property canceledOnTouchOutside = true. look at the example:

Dialog dialog = new Dialog(context)
dialog.setCanceledOnTouchOutside(true);

Solution 4 - Android

It is possible quite easily:

First define your own theme in style.xml:

<style name="DialogSlideAnim" parent="@android:style/Theme.Holo.Dialog">
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowCloseOnTouchOutside">true</item>
</style>

Then in your manifest apply this theme to activity:

    <activity
        android:label="@string/app_name"
        android:name=".MiniModeActivity" 
        android:theme="@style/DialogSlideAnim" >
        <intent-filter >
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

Solution 5 - Android

A combination of Gregory and Matt's answers worked best for me (for Honeycomb and presumably others). This way, views outside will not get touch events when the user tries to touch-outside-cancel the dialog.

In the main Activity, create the touch interceptor in onCreate():

	touchInterceptor = new FrameLayout(this);
	touchInterceptor.setClickable(true); // otherwise clicks will fall through

In onPause() add it:

	if (touchInterceptor.getParent() == null) {
		rootViewGroup.addView(touchInterceptor);
	}

(rootViewGroup may have to be either a FrameLayout or a RelativeLayout. LinearLayout may not work.)

In onResume(), remove it:

	rootViewGroup.removeView(touchInterceptor);

Then, for the dialog-themed Activity, use the code Gregory offered (copied here for your convenience):

public class MyActivity extends Activity {   
   
 @Override   
  protected void onCreate(Bundle savedInstanceState) {   
	super.onCreate(savedInstanceState);   
   
	// Make us non-modal, so that others can receive touch events.   
	getWindow().setFlags(LayoutParams.FLAG_NOT_TOUCH_MODAL, LayoutParams.FLAG_NOT_TOUCH_MODAL);   
   
	// ...but notify us that it happened.   
	getWindow().setFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);   
   
	// Note that flag changes must happen *before* the content view is set.   
	setContentView(R.layout.my_dialog_view);   
  }   
   
  @Override   
  public boolean onTouchEvent(MotionEvent event) {   
	// If we've received a touch notification that the user has touched   
	// outside the app, finish the activity.   
	if (MotionEvent.ACTION_OUTSIDE == event.getAction()) {   
	  finish();   
	  return true;   
	}   
   
	// Delegate everything else to Activity.   
	return super.onTouchEvent(event);   
  }   
}   

Solution 6 - Android

If using a dialog theme like android:theme="@style/Theme.AppCompat.Dialog" or any other dialog theme. On API 11 and after we can use

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        setFinishOnTouchOutside(false);
    }

call this inside onCreate of the activity.

Solution 7 - Android

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Rect dialogBounds = new Rect();
    getWindow().getDecorView().getHitRect(dialogBounds);

    if (!dialogBounds.contains((int) ev.getX(), (int) ev.getY())) {
    	return true;
    }
    return super.dispatchTouchEvent(ev);
}

This code is solved my problem.

Solution 8 - Android

I couldn't get the top answer here to work on a Samsung tab running 3.1, so I did this:

@Override
public boolean onTouchEvent(MotionEvent event) {
	float x = event.getX();
	float y = event.getY();
	int xmargin = (ViewUtils.getScreenWidth() - Constants.PRODUCT_DIALOG_WIDTH) / 2;
	int ymargin = (ViewUtils.getScreenHeight() - Constants.PRODUCT_DIALOG_HEIGHT) / 2;
	
    if (
    		x < xmargin 							 || 
	    	x > ViewUtils.getScreenWidth() - xmargin ||
	    	y < ymargin 							 || 
	    	y > ViewUtils.getScreenHeight() - ymargin
    	) {
    		finish();
    		return true;
    }
    return super.onTouchEvent(event);
}

You'll need to replace Constants.PRODUCT_DIALOG_WIDTH and Constants.PRODUCT_DIALOG_HEIGHT with the width/height of your dialog. Mine was used in a number of places so I made them constants.

You'll also need to implement your own method to get the the screen width and height, which you can find easily on this here site. Don't forget to account for the Android header in that!

It's kind of ugly, and I'm not proud, but it works.

Solution 9 - Android

You can reference the dialog.java code from android source:

public boolean onTouchEvent(MotionEvent event) {
    if (mCancelable && mCanceledOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(event)) {
        cancel();
        return true;
    }
    return false;
}

private boolean isOutOfBounds(MotionEvent event) {
    final int x = (int) event.getX();
    final int y = (int) event.getY();
    final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
    final View decorView = getWindow().getDecorView();
    return (x < -slop) || (y < -slop) || (x > (decorView.getWidth()+slop)) || (y > (decorView.getHeight()+slop));
}

Just modify it to :

public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(event)) {
        finish();
        return true;
    }
    return false;
}

private boolean isOutOfBounds(MotionEvent event) {
    final int x = (int) event.getX();
    final int y = (int) event.getY();
    final int slop = ViewConfiguration.get(this).getScaledWindowTouchSlop();
    final View decorView = getWindow().getDecorView();
    return (x < -slop) || (y < -slop) || (x > (decorView.getWidth() + slop)) || (y > decorView.getHeight() + slop));
}

can solve your problem.

Solution 10 - Android

The only way I got this to work was

alert = new AlertDialog.Builder(this)....

		alert.setOnDismissListener(new DialogInterface.OnDismissListener() {
		@Override
		public void onDismiss(final DialogInterface arg0) {
			Log.i(APP_NAME, "in OnDismissListener");
			// removeDialog(R.layout.dialog3);
			alert.dismiss();
			finish();
		}

i.e. I had to put both dismiss and finish in explicitly, otherwise I ended up with a small white rectangle in the middle of the screen.

Solution 11 - Android

An Activity have dispatchTouchEvent use that

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
	// TODO Auto-generated method stub
	finish();
	return super.dispatchTouchEvent(ev);
	
}

Solution 12 - Android

Just add this item to styles.xml:

<style name="alert_dialog" parent="android:Theme.Dialog">
    <item name="android:windowIsFloating">true</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowFullscreen">false</item>
    <item name="android:windowBackground">@color/float_transparent</item>
    <item name="android:windowAnimationStyle">@null</item>
    <item name="android:backgroundDimEnabled">true</item>
    <item name="android:backgroundDimAmount">0.4</item>
</style>

And in onCreate() and before setContentView:

setTheme(R.style.alert_dialog);

Solution 13 - Android

Using method setFinishOnTouchOutside to enable/disable whether outside is touchable or not.

This is working for activity.

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_yoptions);
    /* your code here */

    // set outside touchable
    this.setFinishOnTouchOutside(true);
}

Solution 14 - Android

For those who want to not close the Dialog Application if touch is outside the Dialog. Add this line

this.setFinishOnTouchOutside(false);

It will not close the dialog box

Solution 15 - Android

Just use this theme. Activity will be dismissed on touch outside.

<style name="DialogTheme" parent="Theme.MaterialComponents.DayNight.Dialog">
    <item name="android:windowIsTranslucent">true</item>
</style>

Solution 16 - Android

Kotlin version worked for me

alert.setOnDismissListener(DialogInterface.OnDismissListener() {
	it.dismiss()
})

Solution 17 - Android

If there's no API support, you should just use a FrameLayout to fill the screen, and manually build a pop-up. Then you can receive focus anywhere on the screen and show/hide views accordingly.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlexView Question on Stackoverflow
Solution 1 - AndroidGregory BlockView Answer on Stackoverflow
Solution 2 - AndroidvabanagasView Answer on Stackoverflow
Solution 3 - AndroidLuis ZandonadiView Answer on Stackoverflow
Solution 4 - AndroidaveprView Answer on Stackoverflow
Solution 5 - AndroidKenView Answer on Stackoverflow
Solution 6 - Androidharsh_vView Answer on Stackoverflow
Solution 7 - AndroidMojiizView Answer on Stackoverflow
Solution 8 - Androiduser901309View Answer on Stackoverflow
Solution 9 - Androidleon.liuView Answer on Stackoverflow
Solution 10 - AndroidtorwalkerView Answer on Stackoverflow
Solution 11 - Androidsharath yadhavView Answer on Stackoverflow
Solution 12 - AndroidMohammad ZaerView Answer on Stackoverflow
Solution 13 - AndroidMAOXUView Answer on Stackoverflow
Solution 14 - AndroidSmaranView Answer on Stackoverflow
Solution 15 - AndroidMuhammed naseef KoppilakkalView Answer on Stackoverflow
Solution 16 - AndroidEugeneView Answer on Stackoverflow
Solution 17 - AndroidMattView Answer on Stackoverflow