Подтвердить что ты не робот

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

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

Сначала у меня есть XML файл, который объявляет макет всплывающего окна popup.xml(текстовое представление внутри 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 и говорит: "Невозможно добавить нулевой токен окна".

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

4b9b3361

Ответ 1

Чтобы избежать 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);
   }
});

Ответ 2

Решение, предоставляемое 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();
}

Ответ 3

Принятый ответ не работал для меня. Я все еще получил BadTokenException. Поэтому я просто вызвал Runnable из обработчика с задержкой как таковой:

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

Ответ 4

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

Ответ 6

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

android:spinnerMode="dialog"

или

Spinner(Context context, int mode)
tnxs RamallahDroid

См. это.

Ответ 7

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

Изменить: один из побочных эффектов: никакого взаимодействия во всплывающем окне для API <= 18, поскольку сенсорные/фокусируемые события будут удалены системой. (http://www.jianshu.com/p/634cd056b90c)

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

Вы также можете попробовать эту проверку:

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

Ответ 11

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

pop.showAtLocation(parentView, ... );

Ответ 12

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

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

Ответ 13

Может быть, пришло время для более нового решения. Этот метод проверяет 5 раз каждые 50 мс, имеет ли маркер родительское представление для PopupWindow. Я использую его внутри своего настроенного PopupWindow.

private fun tryToShowTooltip(tooltipLayout: View) {
    Flowable.fromCallable { parentView.windowToken != null }
            .map { hasWindowToken ->
                if (hasWindowToken) {
                    [email protected] 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
            })
}

с

companion object {

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

class RetryException : Throwable()

Ответ 14

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