Problems creating a Popup Window in Android Activity

AndroidPopupwindow

Android Problem Overview


I'm trying to create a popup window that only appears the first time the application starts. I want it to display some text and have a button to close the popup. However, I'm having troubles getting the PopupWindow to even work. I've tried two different ways of doing it:

First I have an XML file which declares the layout of the popup called popup.xml (a textview inside a linearlayout) and I've added this in the OnCreate() of my main Activity:

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

Second I did the exact same with this 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);

The first throws a NullPointerException and the second throws a BadTokenException and says "Unable to add window -- token null is not valid"

What in the world am I doing wrong? I'm extremely novice so please bear with me.

Android Solutions


Solution 1 - Android

To avoid BadTokenException, you need to defer showing the popup until after all the lifecycle methods are called (-> activity window is displayed):

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

Solution 2 - Android

Solution provided by Kordzik will not work if you launch 2 activities consecutively:

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

If you add popup that way in a case like this, you will get the same crash because ActivityWithPopup won't be attached to Window in this case.

More universal solusion is onAttachedToWindow and onDetachedFromWindow.

And also there is no need for postDelayed(Runnable, 100). Because this 100 millis does not guaranties anything

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

    showPopup();
}

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

    popup.dismiss();
}

Solution 3 - Android

The accepted answer did not work for me. I still received BadTokenException. So I just called the Runnable from a Handler with delay as such:

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

Solution 4 - Android

use class Context eg. MainActivity.this instead of getApplicationContext()

Solution 5 - Android

There are two scenarios when this exception could occur. One is mentioned by kordzik. Other scenario is mentioned here: http://blackriver.to/2012/08/android-annoying-exception-unable-to-add-window-is-your-activity-running/

Make sure you handle both of them

Solution 6 - Android

the solution is to set the spinner mode to dialog as below:

android:spinnerMode="dialog"

or

Spinner(Context context, int mode)
tnxs RamallahDroid

See This.

Solution 7 - Android

Depending on the use case, for types of pop-up to display a message, setting the pop-up type to TYPE_TOAST using setWindowLayoutType() avoids the issue, as this type of pop-up is not dependent on the underlying activity.

Edit: One of the side effects: no interaction in the popup window for API <= 18, as the touchable / focusable events would be removed by the system. ( http://www.jianshu.com/p/634cd056b90c )

I end up with using TYPE_PHONE (as the app happens to have the permission SYSTEM_ALERT_WINDOW, otherwise this won't work too).

Solution 8 - Android

You can check the rootview if it has the token. You can get the parent layout defined from your activity xml, mRootView

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

Solution 9 - Android

Check that findViewById returns something - you might be calling it too early, before the layout is built

Also you may want to post logcat output for the exceptions you're getting

Solution 10 - Android

You can also try to use this check:

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

Solution 11 - Android

If you show a PopupWindow in another PopupWindow, do not use the view in first POP, use the origin parent view.

pop.showAtLocation(parentView, ... );

Solution 12 - Android

I had the same problem (BadTokenException) with AlertDialog on dialog.show(). I was making an AlertDialog by following some example. In my case the reason of that problem was a string dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST)

Everything became working after I removed it.

Solution 13 - Android

Maybe it's time for a newer solution. This methods checks 5 times every 50ms if the parent view for the PopupWindow has a token. I use it inside my customized PopupWindow.

private fun tryToShowTooltip(tooltipLayout: View) {
    Flowable.fromCallable { parentView.windowToken != null }
            .map { hasWindowToken ->
                if (hasWindowToken) {
                    return@map hasWindowToken
                }
                throw RetryException()
            }
            .retryWhen { errors: Flowable<Throwable> ->
                errors.zipWith(
                        Flowable.range(1, RETRY_COUNT),
                        BiFunction<Throwable, Int, Int> { error: Throwable, retryCount: Int ->
                            if (retryCount >= RETRY_COUNT) {
                                throw error
                            } else {
                                retryCount
                            }
                        })
                        .flatMap { retryCount: Int ->
                            Flowable.timer(retryCount * MIN_TIME_OUT_MS, TimeUnit.MILLISECONDS)
                        }
            }
            .onErrorReturn {
                false
            }
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ hasWindowToken ->
                if (hasWindowToken && !isShowing) {
                    showAtLocation(tooltipLayout, Gravity.NO_GRAVITY, 100, 100)
                }
            }, { t: Throwable? ->
                //error logging
            })
}

with

companion object {

    private const val RETRY_COUNT = 5
    private const val MIN_TIME_OUT_MS = 50L
}

class RetryException : Throwable()

Solution 14 - Android

You can specify the y-offset to account for the status bar from the pw.showAtLocation method...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAmplify91View Question on Stackoverflow
Solution 1 - AndroidkordzikView Answer on Stackoverflow
Solution 2 - AndroidDanylo VolokhView Answer on Stackoverflow
Solution 3 - AndroidTodd PaintonView Answer on Stackoverflow
Solution 4 - AndroidbipinView Answer on Stackoverflow
Solution 5 - AndroidTheManView Answer on Stackoverflow
Solution 6 - AndroidsaberView Answer on Stackoverflow
Solution 7 - AndroidheaduckView Answer on Stackoverflow
Solution 8 - AndroidChengView Answer on Stackoverflow
Solution 9 - AndroidAsahiView Answer on Stackoverflow
Solution 10 - AndroidRoman NazarevychView Answer on Stackoverflow
Solution 11 - AndroidYanXing OuView Answer on Stackoverflow
Solution 12 - AndroidMariaView Answer on Stackoverflow
Solution 13 - AndroidCamino2007View Answer on Stackoverflow
Solution 14 - AndroidcipherzView Answer on Stackoverflow