Как отменить диалог, например "Активность", когда он коснулся за пределами окна?

У меня есть активность с темой Dialog, и я хотел бы закрыть (закончить) эту активность, когда кто-то коснется экрана где-нибудь за пределами этого окна активности? Как я могу это сделать?

Ответ 1

Если поддержка API отсутствует, вы должны просто использовать FrameLayout для заполнения экрана и вручную создать всплывающее окно. Затем вы можете получать фокус в любом месте экрана и соответственно отображать/скрывать.

Ответ 2

Просто укажем, что есть способ получить диалоговое поведение "touch out to cancel" из Activity themed в качестве диалога, хотя я не полностью изучил, имеет ли он нежелательные побочные эффекты.

Внутри вашего действия onCreate(), перед созданием представления, вы должны установить два флажка в окне: один, чтобы сделать его "немодальным", чтобы разрешить представлениям, отличным от ваших видов активности, получать события. Во-вторых, необходимо получить уведомление о том, что произошло одно из этих событий, которое отправит вам событие ACTION_OUTSDIE move.

Если вы установите тему в действии на тему диалога, вы получите нужное поведение.

Это выглядит примерно так:

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

Ответ 3

Я нашел еще более простой ответ, который отлично сработал у меня. Если вы используете действие с темой диалога, вы можете применить this.setFinishOnTouchOutside(true); к методу onCreate().

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

Ответ 4

Это очень просто, просто установите свойство canceledOnTouchOutside = true. посмотрите на пример:

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

Ответ 5

Это возможно довольно легко:

Сначала определите свою собственную тему в 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>

Ответ 6

Комбинация ответов Григория и Мэтта работала лучше всего для меня (для сотовых и, предположительно, для других). Таким образом, просмотр снаружи не будет получать события касания, когда пользователь попытается коснуться-вне-отменить диалог.

В основной операции создайте сенсорный перехватчик в 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);   
  }   
}   

Ответ 7

Если вы используете диалоговую тему типа android:theme="@style/Theme.AppCompat.Dialog" или любую другую тему диалога. В API 11 и после того, как мы сможем использовать

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

вызовите это внутри onCreate активности.

Ответ 8

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

Этот код решает мою проблему.

Ответ 9

Я не мог получить главный ответ здесь, чтобы работать на вкладке Samsung с 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 в этом!

Это отвратительно, и я не горжусь, но он работает.

Ответ 10

Вы можете ссылаться на код dialog.java из источника android:

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

может решить вашу проблему.

Ответ 11

Единственный способ, которым я работал, это

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

то есть. Я должен был убрать и уволить, и закончить в явном виде, иначе у меня появился маленький белый прямоугольник в середине экрана.

Ответ 12

В Activity есть функция dispatchTouchEvent, которая

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

}

Ответ 13

Просто добавьте этот элемент в 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);