PopupWindow - Ignorer lorsque vous cliquez à l'extérieur

93

J'ai une PopupWindow sur mon activité, le fait est que ma PopupWindow s'affiche toujours même lorsque j'interagis avec mon activité (disons faire défiler ma liste). Je peux faire défiler ma liste et la fenêtre PopupWindow est toujours là.

Ce que je veux réaliser, c'est lorsque je touche / défile / clique sur / etc sur l'écran qui n'est pas la PopupWindow, je veux fermer la PopupWindow. Tout comme le fonctionnement d'un menu. Si vous avez cliqué en dehors du menu, le menu sera fermé.

J'ai essayé setOutsideTouchable(true)mais cela ne fermera pas la fenêtre. Merci.

villageois
la source

Réponses:

129

S'il vous plaît essayer de mettre setBackgroundDrawablesur PopupWindowqui devrait fermer la fenêtre si vous touchez à l' extérieur de celui - ci.

Marcin S.
la source
5
Je l'ai manqué. Utilisez-vous setBackgroundDrawable sur votre popupWindow? Je sais que définir l'arrière-plan drawable sur null tue le OnTouchListener
Marcin S.
31
c'est tout! thnx man! dans ce cas, même les événements tactiles peuvent être gérés correctement. popupWindow.setOutsideTouchable (vrai); popupWindow.setTouchable (vrai); popupWindow.setBackgroundDrawable (nouveau BitmapDrawable ()); popupWindow.setTouchInterceptor (new OnTouchListener () {@Override public boolean onTouch (View v, MotionEvent event) {if (AppContext.isDebugMode ()) Log.d ("POPUP_WINDOW", "v:" + v.getTag () + " | event: "+ event.getAction ()); popupWindow.dismiss (); return true;}});
beerstorm le
3
Définir background drawable sur null ne fonctionne pas pour moi. Si quelqu'un d'autre a des problèmes, consultez ma réponse.
mpellegr
2
@WareNinja, votre commentaire a fonctionné! Peut-être que vous feriez mieux de laisser une réponse entière à cette question, ce serait utile pour les autres
Anton Kizema
3
@WareNinja BitmapDrawable()est obsolète. Utilisez ColorDrawable()plutôt.
Srujan Barai
125

J'ai trouvé qu'aucune des réponses fournies ne fonctionnait pour moi, à l'exception du commentaire de WareNinja sur la réponse acceptée, et celui de Marcin S. fonctionnera probablement également. Voici la partie qui fonctionne pour moi:

myPopupWindow.setBackgroundDrawable(new BitmapDrawable());
myPopupWindow.setOutsideTouchable(true);

Alternativement:

myPopupWindow.setFocusable(true);

Je ne sais pas quelles sont les différences, mais le code source de ListPopupWindow utilise en réalité ce dernier lorsque sa modalité est définie sur true avec setModal, donc au moins les développeurs Android considèrent cette approche viable, et ce n'est qu'une ligne.

mpellegr
la source
6
Merci beaucoup. Aucune des autres réponses n'a fonctionné pour moi ou ne l'a expliqué assez bien. La deuxième option ne fonctionne pas pour moi cependant.
JDN
2
Je note également que BitmapDrawable est obsolète. Ce serait bien d'avoir une vraie solution au problème, car cela ressemble à une solution de contournement temporaire dont la prise en charge n'est pas garantie dans les nouvelles versions d'API.
HAL9000
pour ne pas utiliser le constructeur obsolète BitmapDrawable, reportez-vous ici: stackoverflow.com/a/21680637/2048266 . popupWindow.setBackgroundDrawable (nouveau BitmapDrawable (getResources (), ""));
nommer
Tout en utilisant la méthode alternative de setFocusable, nous devons cliquer deux fois sur le bouton (où le bouton est placé en dehors de la fenêtre contextuelle) où, comme dans la 1ère méthode, cela fonctionne bien :)
Joy Rex
BitmapDrawable()est déprécié. Utilisez ColorDrawable()plutôt.
Srujan Barai
60

J'ai rencontré les mêmes problèmes et je l'ai corrigé comme ci-dessous. Ça fonctionne bien pour moi.

    // Closes the popup window when touch outside.
    mPopupWindow.setOutsideTouchable(true);
    mPopupWindow.setFocusable(true);
    // Removes default background.
    mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

BTW, n'utilisez pas le constructeur obsolète BitmapDrawable, utilisez ce nouveau ColorDrawable (android.R.color.transparent) pour remplacer l'arrière-plan par défaut.

S'amuser@.@

Luna Kong
la source
3
Assurez-vous d'ajouter ce code avant d'afficher votre popoupWindow
snersesyan
Ai-je vraiment besoin de définir focusable sur true si le popup n'a pas besoin de focus?
Levor
Je suis étonné que cela fonctionne, mais il est requis sur l'API 21. Cela a également corrigé le fait que ma fenêtre contextuelle s'animait incorrectement.
EpicPandaForce
24

Je sais qu'il est tard mais je remarque que les gens ont toujours un problème avec la fenêtre contextuelle. J'ai décidé d'écrire un exemple entièrement fonctionnel où vous pouvez fermer la fenêtre contextuelle en touchant ou en cliquant à l'extérieur de celle-ci ou en touchant simplement la fenêtre elle-même. Pour ce faire, créez une nouvelle classe PopupWindow et copiez ce code:

PopupWindow.class

public class PopupWindow extends android.widget.PopupWindow
{
Context ctx;
Button btnDismiss;
TextView lblText;
View popupView;

public PopupWindow(Context context)
{
    super(context);

    ctx = context;
    popupView = LayoutInflater.from(context).inflate(R.layout.popup, null);
    setContentView(popupView);

    btnDismiss = (Button)popupView.findViewById(R.id.btn_dismiss);
    lblText = (TextView)popupView.findViewById(R.id.text);

    setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
    setWidth(WindowManager.LayoutParams.WRAP_CONTENT);

    // Closes the popup window when touch outside of it - when looses focus
    setOutsideTouchable(true);
    setFocusable(true);

    // Removes default black background
    setBackgroundDrawable(new BitmapDrawable());

    btnDismiss.setOnClickListener(new Button.OnClickListener(){

        @Override
        public void onClick(View v) {


         dismiss();
        }});

    // Closes the popup window when touch it
/*     this.setTouchInterceptor(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            if (event.getAction() == MotionEvent.ACTION_MOVE) {
                dismiss();
            }
            return true;
        }
    }); */   
   } // End constructor

   // Attaches the view to its parent anchor-view at position x and y
   public void show(View anchor, int x, int y)
   {
      showAtLocation(anchor, Gravity.CENTER, x, y);
   }
}

Créez maintenant la mise en page de la fenêtre contextuelle: popup.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout     
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="1dp"
    android:orientation="vertical"
    android:padding="10dp" >

<TextView 
    android:id="@+id/text" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"  
    android:gravity="center" 
    android:padding="5dp" 
    android:text="PopupWindow Example"
    android:textColor="#000000" 
    android:textSize="17sp" 
    android:textStyle="italic" />

<FrameLayout
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:layout_gravity="center_vertical">

    <Button
        android:id="@+id/btn_dismiss" 
        style="?android:attr/buttonStyleSmall" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="Dismiss" 
        android:visibility="gone" />

    <TextView
        android:id="@+id/lbl_dismiss"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="Touch outside of this box to dismiss"
        android:textColor="#ffffff"
        android:textStyle="bold" />

</FrameLayout>      

Dans votre activité principale, créez une instance de la classe PopupWindow:

final PopupWindow popupWindow = new PopupWindow(this);
popupWindow.show(findViewById(R.id.YOUR_MAIN_LAYOUT), 0, -250);

où YOUR_MAIN_LAYOUT est la disposition de l'activité actuelle dans laquelle popupWindow apparaîtra

Marcin S.
la source
1
Merci - cela a fonctionné pour moi. Juste une petite note est d'utiliser plutôt un autre nom autre que PopupWindow pour votre classe personnalisée, appelez-le peut-être MyPopupWindow au lieu de Popupwindow afin qu'android ne soit pas confondu entre votre classe Android standard et votre classe personnalisée.
Simon
@Marcin S. findViewById (R.id.YOUR_MAIN_LAYOUT) ?? Sera-ce R.layout.My_Layout
Ankesh kumar Jaisansaria
@Simon findViewById (R.id.YOUR_MAIN_LAYOUT) ?? Sera-ce R.layout.My_Layout?
Ankesh kumar Jaisansaria
15

Merci pour la réponse de @ LunaKong et la confirmation de @ HourGlass. Je ne veux pas faire de commentaire en double, mais je veux seulement le rendre clair et concis.

// Closes the popup window when touch outside. This method was written informatively in Google's docs.
mPopupWindow.setOutsideTouchable(true);

// Set focus true to prevent a touch event to go to a below view (main layout), which works like a dialog with 'cancel' property => Try it! And you will know what I mean.
mPopupWindow.setFocusable(true);

// Removes default background.
mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

Mttdat.

Nguyen Tan Dat
la source
Je voulais pouvoir fermer la fenêtre contextuelle en cliquant en dehors de celle-ci, mais lorsque je l'ai fait, les vues en dessous (pas une partie de la fenêtre contextuelle, mais une partie de l'activité) étaient cliquées. setFocusabl (true) était ce que je cherchais. Merci!
hellaandrew
@hellaandrew, heureux que cela vous aide, :)
Nguyen Tan Dat
8

Pour un ListPopupWindowensemble, la fenêtre doit être modale lorsqu'elle est affichée.

mListPopupWindow.setModal(true);

De cette façon, cliquer en dehors de la ListPopupWindowfera disparaître.

toobsco42
la source
Merci, je cherchais juste ça. Cela permet non seulement de désactiver listpopupwindow après avoir touché à l'extérieur de la vue, mais également de ne pas transmettre l'événement touch à d'autres vues qui se trouvent à côté de listpopwindow. Je cherchais désespérément cela, car dans mon cas, toucher à l'extérieur de listpopwindow passait l'événement à recyclerview qui était en dessous à côté de rejeter listpopupwindow, et l'élément recyclerview était sélectionné, ce que je ne voulais pas.
shankar_vl
Vous devrez peut-être également mListPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);empêcher la fenêtre contextuelle d'interférer avec le clavier à l'écran.
Mr-IDE
6

Notez que pour annuler avec popupWindow.setOutsideTouchable(true), vous devez définir la largeur et la hauteur wrap_contentcomme le code ci-dessous:

PopupWindow popupWindow = new PopupWindow(
            G.layoutInflater.inflate(R.layout.lay_dialog_support, null, false),
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT, true);

popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
popupWindow.showAtLocation(view, Gravity.RIGHT, 0, 0);
Hadi Note
la source
5
  popupWindow.setTouchable(true);
  popupWindow.setFocusable(true);
  popupWindow.showAtLocation(popupView, Gravity.CENTER, 0, 0);

Il supprimera la fenêtre contextuelle lorsque vous cliquez / touchez sur l'écran.Assurez-vous que vous avez défini focusable true avant showAtLocation.

Android
la source
1
Veuillez ajouter un texte explicatif pour expliquer comment cela fournit une réponse précise à la question posée. Merci.
philantrovert
Merci! Vous devez appeler les setters avant d'appeler showAtLocation ().
droid256
5

Vous pouvez utiliser isOutsideTouchable OU isFocusable pour afficher la fenêtre contextuelle lorsque vous touchez à l'extérieur

popupWindow.isOutsideTouchable = true // dismiss popupwindow when touch outside

popupWindow.isFocusable = true // dismiss popupwindow when touch outside AND when press back button

Remarque

  • Actuellement, après le test, je vois que setBackgroundDrawable cela ne nous aide pas à fermer la fenêtre contextuelle

  • Si vous regardez le code pour ignorer dans PopupWindow( PopupWindow->PopupDecorView->dispatchKeyEventet PopupWindow->PopupDecorView->onTouchEvent). Vous verrez que lorsque le bouton de retour de la presse, ils écartent sur ACTION_UPet en dehors en contact qu'ils rejettent sur ACTION_UPouACTION_OUTSIDE

Phan Van Linh
la source
4
mPopWindow.setFocusable(true);
Muhammad Aamir Ali
la source
1
C'est la seule chose requise. Je ne comprends pas pourquoi la réponse acceptée est si fortement votée.
sziraqui
4

Les suggestions de @LunaKong fonctionnent comme un charme.

Mais la configuration de mPopupWindow.setFocusable (false). supprime le toucher inutile nécessaire pour faire disparaître la fenêtre contextuelle.

Par exemple: considérons qu'une fenêtre contextuelle est visible à l'écran et que vous êtes sur le point de cliquer sur un bouton. Donc, dans ce cas, (si mpopwindow.setFocusable (true)) au premier clic d'un bouton, popupwindow se fermera. Mais vous devez cliquer à nouveau pour que le bouton fonctionne. if ** (mpopwindwo.setFocusable (false) ** un simple clic sur le bouton ferme la fenêtre contextuelle et déclenche le clic du bouton. J'espère que cela aide.

Sablier
la source
1
Merci beaucoup! Je cherchais exactement la même chose
Ganesh
3

Définissez l'arrière-plan de la fenêtre transparent:

PopupWindow.getBackground().setAlpha(0);

Après avoir défini votre arrière-plan dans la mise en page. Fonctionne très bien.

amak
la source
1
getBackground () peut être nul.
Jared Rummler
1

Dans certains cas, il n'est pas souhaitable de rendre le popup focusable (par exemple, vous ne voudrez peut-être pas qu'il vole le focus d'une autre vue).

Une autre approche consiste à utiliser un intercepteur tactile:

popupWindow.setOutsideTouchable(true);
popupWindow.setTouchInterceptor(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
            popupWindow.dismiss();
        }
        return false;
    }
});
dev.bmax
la source
1

Si cette fenêtre contextuelle est une autre activité, et que vous avez réduit sa taille à l'écran d'origine et que vous souhaitez activer ou désactiver la zone extérieure, vous pouvez simplement activer ou désactiver la zone extérieure à l'aide de ce code:

activer:

YourActivity.this.setFinishOnTouchOutside(true);

désactiver:

YourActivity.this.setFinishOnTouchOutside(false);

Yasser Elgreatly
la source
0

Utilisez View popupView pour fermer la fenêtre popup

`popupView.setOnClickListener(new View.OnClickListener() {
                   @Override
                   public void onClick(View view) {
                       popupWindow.dismiss();
                   }
               }); 

`Si vous utilisez ceci, vous pouvez également définirOnClickListener sur n'importe quel bouton dans la fenêtre contextuelle

Prabhat Kumar Singh
la source