如何在窗外触摸时取消以Activity为主题的对话框?

时间:2011-01-10 18:46:23

标签: android dialog android-activity touch

我有一个带有Dialog主题的活动,当有人在此活动窗口外的任何地方触摸屏幕时,我想关闭(完成)此活动?我怎么能这样做?

17 个答案:

答案 0 :(得分:96)

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

在Activity的onCreate()方法中,在创建视图之前,您将在窗口上设置两个标志:一个使其为“非模态”,以允许活动视图以外的视图接收事件。第二个是接收其中一个事件发生的通知,这将向您发送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);
  }
}

答案 1 :(得分:68)

我找到了一个更简单的答案,对我来说非常有效。如果您正在使用具有对话框主题的活动,则可以将this.setFinishOnTouchOutside(true);应用于活动的onCreate()方法。

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

答案 2 :(得分:27)

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

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

答案 3 :(得分:18)

很容易:

首先在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>

答案 4 :(得分:8)

Gregory和Matt的答案组合最适合我(对于Honeycomb和大概是其他人)。这样,当用户尝试触摸外部取消对话框时,外部视图将不会获得触摸事件。

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

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

然后,对于以对话框为主题的Activity,请使用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);   
  }   
}   

答案 5 :(得分:6)

如果使用if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { setFinishOnTouchOutside(false); } 之类的对话框主题或任何其他对话框主题。 在API 11上以及之后我们可以使用

onCreate

在活动的{{1}}内调用此内容。

答案 6 :(得分:4)

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

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

答案 7 :(得分:3)

您可以从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));
}

可以解决您的问题。

答案 8 :(得分:3)

我无法在这里得到最佳答案,可以在运行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);
}

您需要将Constants.PRODUCT_DIALOG_WIDTH和Constants.PRODUCT_DIALOG_HEIGHT替换为对话框的宽度/高度。我曾在很多地方使用过,所以我把它们做成了常数。

您还需要实现自己的方法来获取屏幕宽度和高度,您可以在此网站上轻松找到。不要忘记考虑Android标题!

这有点难看,我并不自豪,但它有效。

答案 9 :(得分:2)

我让这个工作的唯一方法是

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

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

答案 10 :(得分:0)

一个Activity有dispatchTouchEvent使用那个

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

}

答案 11 :(得分:0)

只需将此项添加到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);

答案 12 :(得分:0)

使用方法setFinishOnTouchOutside启用/禁用外部是否可触摸。

这适用于活动

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

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

答案 13 :(得分:0)

对于那些不想在对话框外部触摸而关闭对话框应用程序的人。 添加此行

this.setFinishOnTouchOutside(false);

它不会关闭对话框

答案 14 :(得分:0)

只需使用此主题。触摸外部后,活动将被取消。

@Override
public Page<PaymentLog> getPaymentLog(int status, Pageable pageable) {
    return paymentLogRepository.getAllByStatus(status, pageable);
}

答案 15 :(得分:0)

Kotlin 版本对我有用

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

答案 16 :(得分:-5)

如果没有API支持,您应该使用FrameLayout填充屏幕,并手动构建弹出窗口。然后,您可以在屏幕上的任何位置接收焦点,并相应地显示/隐藏视图。