Проблемы с созданием всплывающего окна в Android Activity

Я пытаюсь создать всплывающее окно, которое появляется только при первом запуске приложения. Я хочу, чтобы он отображал текст и имел кнопку для закрытия всплывающего окна. Однако у меня возникли проблемы с тем, чтобы заставить всплывающее окно работать. Я пробовал два разных способа сделать это:

сначала у меня есть XML-файл, который объявляет макет всплывающего окна popup.xml (textview внутри linearlayout), и я добавил Это в OnCreate () моего основного Активность:

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

во-вторых, я сделал то же самое с этим кодом:

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

первый бросает исключение NullPointerException, а второй бросает исключение BadTokenException и говорит: "невозможно добавить окно-токен null недопустим"

что я делаю не так? Я очень новичок, поэтому, пожалуйста, медведь со мной.

13 ответов


чтобы избежать BadTokenException, вам нужно отложить отображение всплывающего окна до тех пор, пока не будут вызваны все методы жизненного цикла (-> отображается окно активности):

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

решение, предоставленное Kordzik, не будет работать, если вы запустите 2 действия последовательно:

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

Если вы добавите всплывающее окно таким образом в таком случае, вы получите тот же сбой, потому что ActivityWithPopup не будет прикреплен к окну в этом случае.

более универсальным решением является onAttachedToWindow и onDetachedFromWindow.

а также нет необходимости в postDelayed (Runnable, 100). Потому что это 100 Миллис не гарантирует ничего

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

    showPopup();
}

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

    popup.dismiss();
}

Я обнаружил, что принятый ответ не работает для меня. Я все еще получил ошибку bad token.. Поэтому я просто вызвал runnable из обработчика с задержкой как таковой..

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

есть два сценария, когда это исключение может произойти. Один упоминается kordzik. Другой сценарий упоминается здесь: http://blackriver.to/2012/08/android-annoying-exception-unable-to-add-window-is-your-activity-running/

убедитесь, что вы справитесь с ними обоими


решение состоит в том, чтобы установить режим spinner в диалоговое окно, как показано ниже:

android:spinnerMode="dialog"

или

Spinner(Context context, int mode)
tnxs RamallahDroid

Это Видеть.


в зависимости от варианта использования для типов всплывающих окон для отображения сообщения Установите тип всплывающего окна в TYPE_TOAST с помощью setWindowLayoutType() избегает проблемы, так как этот тип всплывающего окна не зависит от базовой активности.

Edit: один из побочных эффектов: нет взаимодействия во всплывающем окне для API http://www.jianshu.com/p/634cd056b90c )

в конечном итоге я использую TYPE_PHONE (как приложение имеет разрешение SYSTEM_ALERT_WINDOW, иначе это тоже не сработает).


вы можете проверить rootview, если у него есть токен. Вы можете получить Родительский макет, определенный из вашего действия xml, mRootView

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

используйте контекст класса, например. В MainActivity.это вместо getApplicationContext()


проверяем, что findViewById возвращает что - то-вы можете вызвать его слишком рано, прежде чем макет будет построен

также вы можете опубликовать вывод logcat для исключений, которые вы получаете


вы также можете попробовать использовать эту проверку:

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

Если вы показываете всплывающее окно в другом всплывающем окне, не используйте представление в первом POP, используйте родительское представление origin.

pop.showAtLocation(parentView, ... );

у меня была та же проблема (BadTokenException) с AlertDialog на dialog.show(). Я делал AlertDialog, следуя некоторому примеру. В моем случае причиной этой проблемы была строка dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST)

все стало работать после того, как я удалил его.


вы можете указать y-смещение для учета строки состояния из pw.showAtLocation метод...