web-dev-qa-db-ja.com

Android Activityでポップアップウィンドウを作成する際の問題

アプリケーションの初回起動時にのみ表示されるポップアップウィンドウを作成しようとしています。テキストを表示し、ポップアップを閉じるボタンが必要です。ただし、PopupWindowを機能させるのに問題があります。私はそれを行う2つの異なる方法を試しました。

最初に、popup.xml(linearlayout内のテキストビュー)と呼ばれるポップアップのレイアウトを宣言するXMLファイルがあり、これをメインアクティビティの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をスローし、2番目はBadTokenExceptionをスローして、「ウィンドウを追加できません-トークンnullは無効です」と表示します

私は間違って何をしているのですか?私は非常に初心者なので、我慢してください。

52
Amplify91

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);
   }
});
175
kordzik

Kordzikが提供するソリューションは、2つのアクティビティを連続して起動すると機能しません。

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

このような場合にポップアップをそのように追加すると、ActivityWithPopupはこの場合Windowにアタッチされないため、同じクラッシュが発生します。

より普遍的な解決策は、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();
}
29
Danylo Volokh

受け入れられた答えは私にはうまくいきませんでした。まだBadTokenExceptionを受け取っています。そのため、ハンドラーからRunnableを遅延付きで呼び出しました。

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

クラスContextを使用します。 getApplicationContext()の代わりにMainActivity.this

9
bipin

この例外が発生する可能性のあるシナリオは2つあります。 1つはkordzikによって言及されています。他のシナリオはここに記載されています: http://blackriver.to/2012/08/Android-annoying-exception-unable-to-add-window-is-your-activity-running/

必ず両方を処理してください

4
TheMan

解決策は、スピナーモードを次のようにダイアログに設定することです。

Android:spinnerMode="dialog"

または

Spinner(Context context, int mode)
tnxs RamallahDroid

こちらをご覧ください

4
saber

ユースケースによっては、ポップアップの種類がメッセージを表示する場合、setWindowLayoutType()を使用してポップアップの種類をTYPE_TOASTに設定すると、この種類のポップアップは基礎となるものに依存しないため、問題を回避できます。アクティビティ。

編集:副作用の1つ:タッチ可能/フォーカス可能イベントがシステムによって削除されるため、API <= 18のポップアップウィンドウでの対話はありません。 ( http://www.jianshu.com/p/634cd056b90c

最終的にTYPE_PHONEを使用することになります(アプリにSYSTEM_ALERT_WINDOWのアクセス許可が付与されているため、そうでない場合も機能しません)。

1
headuck

ルートビューにトークンがある場合は、ルートビューを確認できます。アクティビティxml、mRootViewから定義された親レイアウトを取得できます。

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

別のPopupWindowにPopupWindowを表示する場合、最初のPOPでビューを使用せず、Origin親ビューを使用します。

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

このチェックを使用することもできます:

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

findViewByIdが何かを返すことを確認してください-レイアウトが構築される前に、それを呼び出すのが早すぎるかもしれません

また、取得している例外のlogcat出力を投稿することもできます。

0
Asahi

たぶん、新しいソリューションの時間です。このメソッドは、PopupWindowの親ビューにトークンがある場合、50msごとに5回チェックします。カスタマイズした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
            })
}

companion object {

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

class RetryException : Throwable()
0
Camino2007

dialog.show()のAlertDialogで同じ問題(BadTokenException)が発生しました。私はいくつかの例に従ってAlertDialogを作成していました。私の場合、その問題の理由は文字列dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST)でした

私はそれを削除した後、すべてが動作するようになりました。

0
Maria