web-dev-qa-db-fra.com

Problèmes lors de la création d'une fenêtre contextuelle dans l'activité Android

J'essaie de créer une fenêtre contextuelle qui n'apparaît que lors du premier démarrage de l'application. Je veux qu'il affiche du texte et un bouton pour fermer la fenêtre contextuelle. Cependant, j'ai du mal à faire fonctionner PopupWindow. J'ai essayé deux façons différentes de le faire:

Premièrement, j'ai un fichier XML qui déclare la mise en page de la popup appelée popup.xml (une vue de texte dans un affichage linéaire) et je l'ai ajouté dans le OnCreate () de mon activité principale:

PopupWindow pw = new PopupWindow(findViewById(R.id.popup), 100, 100, true);
    pw.showAtLocation(findViewById(R.id.main), Gravity.CENTER, 0, 0);

Deuxièmement, j'ai fait exactement la même chose avec ce code:

final LayoutInflater inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    PopupWindow pw = new PopupWindow(inflater.inflate(R.layout.popup, (ViewGroup) findViewById(R.layout.main) ), 100, 100, true);
    pw.showAtLocation(findViewById(R.id.main_page_layout), Gravity.CENTER, 0, 0);

Le premier lève une exception NullPointerException et le second lève une BadTokenException et dit "Impossible d'ajouter fenêtre - le jeton null n'est pas valide"

Qu'est-ce que je fais mal au monde? Je suis extrêmement novice, alors s'il te plaît, supporte-moi.

47
Amplify91

Pour éviter BadTokenException, vous devez différer l'affichage de la fenêtre contextuelle jusqu'à ce que toutes les méthodes de cycle de vie aient été appelées (la fenêtre -> activité s'affiche):

 findViewById(R.id.main_page_layout).post(new Runnable() {
   public void run() {
     pw.showAtLocation(findViewById(R.id.main_page_layout), Gravity.CENTER, 0, 0);
   }
});
170
kordzik

La solution fournie par Kordzik ne fonctionnera pas si vous lancez deux activités consécutives:

startActivity(ActivityWithPopup.class);
startActivity(ActivityThatShouldBeAboveTheActivivtyWithPopup.class);

Si vous ajoutez ce type de message dans un cas comme celui-ci, vous obtiendrez le même blocage, car ActivityWithPopup ne sera pas attaché à Window dans ce cas.

Une solution plus universelle est onAttachedToWindow et onDetachedFromWindow .

Et aussi il n'y a pas besoin de postDelayed (Runnable, 100). Parce que ces 100 millis ne garantissent rien

@Override
public void onAttachedToWindow() {
    super.onAttachedToWindow();
    Log.d(TAG, "onAttachedToWindow");

    showPopup();
}

@Override
public void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    Log.d(TAG, "onDetachedFromWindow");

    popup.dismiss();
}
24
Danylo Volokh

J'ai trouvé que la réponse acceptée ne fonctionnait pas pour moi. J'ai quand même reçu l'erreur de jeton .. Je viens d'appeler le runnable depuis un gestionnaire avec un délai en tant que tel ..

new Handler().postDelayed(new Runnable() {
    public void run() {
        showPopup();
    }
}, 100);
15
Todd Painton

utiliser la classe Contexte par exemple. MainActivity.this au lieu de getApplicationContext ()

5
bipin

Il existe deux scénarios dans lesquels cette exception pourrait se produire. L'un est mentionné par kordzik. Un autre scénario est mentionné ici: http://blackriver.to/2012/08/Android-annoying-exception-unable-to-add-window-is-your-activity-running/

Assurez-vous de bien gérer les deux

4
TheMan

la solution consiste à définir le mode de rotation sur dialogue comme ci-dessous:

Android:spinnerMode="dialog"

ou

Spinner(Context context, int mode)
tnxs RamallahDroid

Regarde ça.

3
saber

Selon le cas d'utilisation, pour que les types de fenêtres contextuelles affichent un message, définir le type de fenêtre contextuelle sur TYPE_TOAST à l'aide de setWindowLayoutType() évite le problème, car ce type de fenêtre contextuelle ne dépend pas de l'activité sous-jacente.

Edit: Un des effets secondaires: aucune interaction dans la fenêtre contextuelle de l’API <= 18, les événements touchables/focusables étant supprimés par le système. ( http://www.jianshu.com/p/634cd056b90c )

Je finis par utiliser TYPE_PHONE (étant donné que l'application dispose de l'autorisation SYSTEM_ALERT_WINDOW, sinon cela ne fonctionnera pas aussi).

1
headuck

Vous pouvez vérifier la vue racine si elle contient le jeton. Vous pouvez obtenir la mise en page parent définie à partir de votre activité xml, mRootView.

if (mRootView != null && mRootView.getWindowToken() != null) {
    popupWindow.showAtLocation();
}
1
Cheng

Si vous affichez une PopupWindow dans une autre PopupWindow, n'utilisez pas la vue dans le premier POP, utilisez la vue Parent d'origine.

pop.showAtLocation(parentView, ... );
0
YanXing Ou

Vous pouvez également essayer d'utiliser cette vérification:

  public void showPopupProgress (){
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            if (getWindow().getDecorView().getWindowVisibility() == View.GONE) {
                showPopupProgress();
                return;
            }
            popup.showAtLocation(.....);
        }
    });
}
0
Lemberg

Vérifiez que findViewById renvoie quelque chose - vous l'appelez peut-être trop tôt, avant la construction de la présentation

Aussi, vous pouvez publier la sortie logcat pour les exceptions que vous obtenez 

0
Asahi