Reputation: 2233
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 ?
Upvotes: 50
Views: 85301
Reputation: 577
Kotlin version worked for me
alert.setOnDismissListener(DialogInterface.OnDismissListener() {
it.dismiss()
})
Upvotes: 0
Reputation: 2194
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>
Upvotes: 0
Reputation: 1651
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
Upvotes: 0
Reputation: 389
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);
}
Upvotes: 0
Reputation: 696
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);
Upvotes: 0
Reputation: 3269
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.
Upvotes: 6
Reputation: 546
An Activity have dispatchTouchEvent use that
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
finish();
return super.dispatchTouchEvent(ev);
}
Upvotes: 0
Reputation: 351
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.
Upvotes: 3
Reputation: 7958
@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.
Upvotes: 4
Reputation: 41
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.
Upvotes: 3
Reputation: 811
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);
}
Upvotes: 77
Reputation:
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.
Upvotes: 3
Reputation: 1896
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>
Upvotes: 19
Reputation: 2964
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);
}
}
Upvotes: 8
Reputation: 667
It's very simple, just set the property canceledOnTouchOutside = true
. look at the example:
Dialog dialog = new Dialog(context)
dialog.setCanceledOnTouchOutside(true);
Upvotes: 37
Reputation: 1154
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);
}
}
Upvotes: 101
Reputation: 3847
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.
Upvotes: -2