如何取消一个类似活动的对话窗口在窗口外面?

我有一个Dialog主题的活动,当有人在屏幕上的任何地方触摸屏幕时,我想closures(完成)这个活动。 我该怎么做?

如果没有API支持,则只需使用FrameLayout填充屏幕,然后手动构build一个popup窗口。 然后,您可以在屏幕上的任何位置获得焦点,并相应地显示/隐藏视图。

只是要指出,有一种方法可以从一个以对话框为主的活动中获得对话框样的“触摸外部取消”行为,尽pipe我没有完全调查它是否有不需要的副作用。

在你的Activity的onCreate()方法中,在创build视图之前,你需要在窗口上设置两个标志:一个是非模态的,允许除活动视图以外的视图接收事件。 第二个是接收通知,其中一个事件已经发生,它将向您发送一个ACTION_OUTSDIE移动事件。

如果将活动的主题设置为对话框主题,则会得到所需的行为。

它看起来像这样:

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); } } 

我发现了一个更简单的答案,完全适合我。 如果你正在使用对话框主题的活动,那么你可以应用this.setFinishOnTouchOutside(true); 到activity的onCreate()方法。

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

这很简单,只需设置属性canceledOnTouchOutside = true 。 看看这个例子:

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

这很容易:

首先在style.xml中定义你自己的主题:

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

然后在您的清单中将这个主题应用于活动:

  <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> 

格雷戈里和马特的答案组合对我来说效果最好(对于蜂窝和大概其他人)。 这样,当用户尝试触摸外部取消对话框时,外部视图将不会获得触摸事件。

在主Activity中,在onCreate()中创build触摸拦截器:

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

在onPause()中添加它:

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

(rootViewGroup可能必须是一个FrameLayout或一个RelativeLayout,LinearLayout可能无法工作。)

在onResume()中,将其删除:

  rootViewGroup.removeView(touchInterceptor); 

然后,以对话为主题的活动,请使用Gregory提供的代码(复制在这里以方便您):

 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); } } 
 @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); } 

这个代码解决了我的问题。

如果使用像android:theme="@style/Theme.AppCompat.Dialog"或任何其他对话框主题的对话框主题。 在API 11之后,我们可以使用

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

在这个里面调用这个活动的创build。

我无法得到最好的答案在这里运行3.1的三星标签,所以我这样做:

 @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); } 

您需要用对话框的宽度/高度replaceConstants.PRODUCT_DIALOG_WIDTH和Constants.PRODUCT_DIALOG_HEIGHT。 我在一些地方使用了矿井,所以我把它们作为常量。

您还需要实现自己的方法来获取屏幕宽度和高度,您可以在此处轻松find此屏幕的宽度和高度。 不要忘记在Android头部帐户!

这是一种丑陋的,我并不自豪,但它的作品。

你可以参考android源码中的dialog.java代码:

 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)); } 

只要将其修改为:

 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)); } 

可以解决你的问题。

我得到这个工作的唯一方法是

 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(); } 

即我必须明确地放弃和完成,否则我会在屏幕中间出现一个白色的小矩形。

一个Activity有dispatchTouchEvent使用

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

只需将这个项目添加到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> 

onCreate()setContentView之前:

 setTheme(R.style.alert_dialog);