web-dev-qa-db-ja.com

ウィンドウの外側をタッチしたときに、アクティビティのようなテーマのダイアログをキャンセルする方法は?

Dialogテーマのアクティビティがあり、誰かがこのアクティビティのウィンドウの外のどこかで画面に触れたら、このアクティビティを閉じます(終了します)?これどうやってするの ?

44
Alex

isダイアログをテーマにしたアクティビティからダイアログのような「タッチしてキャンセルする」動作を取得する方法を指摘するだけですが、望ましくない副作用があるかどうかは完全には調査していません。

アクティビティのonCreate()メソッド内で、ビューを作成する前に、ウィンドウに2つのフラグを設定します。1つは「非モーダル」にし、アクティビティのビュー以外のビューがイベントを受信できるようにします。 2番目は、それらのイベントの1つが発生したという通知を受信することです。これにより、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);
  }
}
98
Gregory Block

さらに簡単な答えが見つかりました。ダイアログテーマでアクティビティを使用している場合は、this.setFinishOnTouchOutside(true);をアクティビティのonCreate()メソッドに適用できます。

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

プロパティを設定するだけで非常に簡単ですcanceledOnTouchOutside = true。例を見てください:

Dialog dialog = new Dialog(context)
dialog.setCanceledOnTouchOutside(true);
29
Luis Zandonadi

非常に簡単に可能です:

まず、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>
18
alex.veprik

グレゴリーとマットの回答の組み合わせは、私にとって(ハニカムおよびおそらく他の人にとって)最適に機能しました。この方法では、ユーザーがダイアログの外側をタッチしてキャンセルしようとしたときに、外側のビューはタッチイベントを取得しません。

メインアクティビティで、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);

次に、ダイアログをテーマにしたアクティビティのために、提供されたコード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);   
  }   
}   
8
Ken

Android:theme="@style/Theme.AppCompat.Dialog"などのダイアログテーマまたはその他のダイアログテーマを使用している場合。 API 11以降では使用できます

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

アクティビティのonCreate内でこれを呼び出します。

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

このコードは私の問題を解決します。

4
Mojiiz

Dialog.Javaコードは、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));
}

次のように変更するだけです。

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
leon.liu

ここでは、3.1を実行しているSamsungタブで作業するためのトップアンサーを取得できなかったため、次のようにしました。

@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ヘッダーを考慮することを忘れないでください!

それはちょっといですし、私は誇りに思っていませんが、うまくいきます。

3
user901309

これを機能させる唯一の方法は

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

つまり、明示的に終了と終了の両方を入力する必要がありました。そうしないと、画面の中央に小さな白い長方形ができてしまいました。

2
torwalker

タッチがダイアログの外にある場合、ダイアログアプリケーションを閉じたくない場合。この行を追加

this.setFinishOnTouchOutside(false);

ダイアログボックスは閉じません。

0
Smaran

この項目を_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);
_
0
Hadid Graphics

アクティビティはdispatchTouchEventにそれを使用させます

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

}
0
sharath yadhav

メソッドsetFinishOnTouchOutsideを使用して、外部がタッチ可能かどうかを有効/無効にします。

これは活動のために働いています

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

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