Как отменить диалоговое окно, например «Активность», если коснуться за пределами окна? - PullRequest
44 голосов
/ 10 января 2011

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

Ответы [ 15 ]

96 голосов
/ 29 апреля 2011

Просто для того, чтобы указать, что - это способ получить похожее на диалог поведение "прикосновение снаружи, чтобы отменить" из действия в виде диалога в виде темы, хотя я не до конца исследовал, имеет ли он нежелательную сторону эффекты.

В методе 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);
  }
}
68 голосов
/ 03 января 2013

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

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_yoptions);
    this.setFinishOnTouchOutside(true);
}
27 голосов
/ 21 июля 2011

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

Dialog dialog = new Dialog(context)
dialog.setCanceledOnTouchOutside(true);
18 голосов
/ 27 июня 2012

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

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

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

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

Затем для диалоговой темы используйте код, предложенный Григорием (скопирован здесь для вашего удобства):

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);   
  }   
}   
6 голосов
/ 09 января 2016

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

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

, вызывать это внутри onCreate действия.

4 голосов
/ 23 ноября 2013
@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);
}

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

3 голосов
/ 31 марта 2013

Вы можете сослаться на код 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));
}

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

3 голосов
/ 27 июня 2012

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

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

2 голосов
/ 05 апреля 2014

Единственный способ заставить это работать -

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

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

...