web-dev-qa-db-fra.com

Comment annuler un dialogue ayant pour thème Activité tel que touché en dehors de la fenêtre?

J'ai une activité avec un thème de dialogue et je voudrais terminer cette activité quand quelqu'un touche l'écran quelque part en dehors de la fenêtre de cette activité? Comment puis-je faire ceci ?

44
Alex

Il suffit de souligner que est un moyen d’obtenir un comportement semblable à un dialogue «effacer de l’extérieur pour annuler» à partir d’un thème ayant pour thème une activité, même si je n’ai pas encore étudié en détail les effets secondaires indésirables.

Dans la méthode onCreate () de votre activité, avant de créer la vue, vous allez définir deux indicateurs sur la fenêtre: l'un pour la rendre "non modale", pour permettre aux vues autres que celles de votre activité de recevoir des événements. La seconde consiste à recevoir une notification indiquant qu'un de ces événements a eu lieu, ce qui vous enverra un événement de déplacement ACTION_OUTSDIE.

Si vous définissez le thème de l'activité sur le thème de la boîte de dialogue, vous obtiendrez le comportement souhaité.

Cela ressemble à quelque chose comme ça:

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);
  }
}
94
Gregory Block

J'ai trouvé une réponse encore plus simple qui a parfaitement fonctionné pour moi. Si vous utilisez une activité avec le thème de boîte de dialogue, vous pouvez appliquer this.setFinishOnTouchOutside(true); à la méthode onCreate () de l'activité. 

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

C'est très simple, il suffit de définir la propriété canceledOnTouchOutside = true. Regardez l'exemple:

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

C'est possible assez facilement:

Commencez par définir votre propre thème dans style.xml:

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

Puis, dans votre manifeste, appliquez ce thème à l'activité:

    <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

Une combinaison des réponses de Gregory et de Matt a fonctionné le mieux pour moi (pour Honeycomb et vraisemblablement pour d’autres). De cette façon, les vues extérieures ne recevront pas d'événements tactiles lorsque l'utilisateur essaiera de toucher, de l'extérieur, d'annuler la boîte de dialogue.

Dans l'activité principale, créez l'intercepteur tactile dans onCreate ():

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

Dans onPause () ajoutez-le:

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

(rootViewGroup peut être soit un FrameLayout, soit un RelativeLayout. LinearLayout peut ne pas fonctionner.)

Dans onResume (), supprimez-le:

    rootViewGroup.removeView(touchInterceptor);

Ensuite, pour l’activité sur le thème du dialogue, utilisez le code proposé par Gregory (copié ici pour votre commodité):

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

Si vous utilisez un thème de dialogue tel que Android:theme="@style/Theme.AppCompat.Dialog" ou tout autre thème de dialogue . Sur API 11 et les versions ultérieures 

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

appelez cela à l'intérieur onCreate de l'activité.

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

Ce code est résolu mon problème.

4
Mojiiz

Vous pouvez référencer le code dialog.Java à partir de la source 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));
}

Il suffit de le modifier pour:

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

peut résoudre votre problème.

3
leon.liu

Je ne pouvais pas obtenir la meilleure réponse ici pour travailler sur un onglet Samsung fonctionnant sous 3.1, alors j'ai fait ceci:

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

Vous devrez remplacer Constants.PRODUCT_DIALOG_WIDTH et Constants.PRODUCT_DIALOG_HEIGHT par la largeur/hauteur de votre boîte de dialogue. Le mien a été utilisé à plusieurs endroits, alors je leur ai fait des constantes.

Vous aurez également besoin de mettre en œuvre votre propre méthode pour obtenir la largeur et la hauteur de l'écran, que vous pouvez trouver facilement sur ce site. N'oubliez pas de rendre compte de l'en-tête Android à cet égard!

C'est un peu moche et je ne suis pas fier, mais ça marche.

3
user901309

La seule façon pour que je travaille est

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

c’est-à-dire que j’ai dû mettre à la fois le renvoi et la finition de manière explicite, sinon j’ai fini avec un petit rectangle blanc au milieu de l’écran.

2
torwalker

Pour ceux qui veulent ne pas fermer l'application de dialogue si le contact est en dehors du dialogue. Ajouter cette ligne

this.setFinishOnTouchOutside(false);

Cela ne fermera pas la boîte de dialogue 

0
Smaran

Ajoutez simplement cet article à 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>

Et dans onCreate() et avant setContentView:

setTheme(R.style.alert_dialog);
0
Hadid Graphics

Une activité que dispatchTouchEvent utilise

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

}
0
sharath yadhav

Utiliser la méthode setFinishOnTouchOutside pour activer/désactiver si l'extérieur est touchable ou non.

Cela fonctionne pour l'activité.

@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