55

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

まず、popup.xml というポップアップのレイアウトを宣言する 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);

1 つ目は NullPointerException をスローし、2 つ目は BadTokenException をスローして、「ウィンドウを追加できません -- トークン null が無効です」と表示します。

私は一体何を間違っているのでしょうか?私は非常に初心者ですので、ご容赦ください。

4

14 に答える 14

187

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);
   }
});
于 2011-01-17T13:09:17.110 に答える
34

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

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

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

より普遍的なソリューションはonAttachedToWindowonDetachedFromWindowです。

また、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();
}
于 2015-11-19T16:53:46.797 に答える
1

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

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

私はTYPE_PHONEを使用することになります(アプリにはたまたま権限SYSTEM_ALERT_WINDOWがあるため、そうでなければこれも機能しません)。

于 2015-12-14T11:27:10.157 に答える
0

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

  public void showPopupProgress (){
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            if (getWindow().getDecorView().getWindowVisibility() == View.GONE) {
                showPopupProgress();
                return;
            }
            popup.showAtLocation(.....);
        }
    });
}
于 2014-12-29T14:00:51.223 に答える
0

CheckfindViewByIdは何かを返します - レイアウトが構築される前に呼び出すのが早すぎる可能性があります

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

于 2010-11-15T20:30:29.663 に答える
0

多分それは新しい解決策の時です。このメソッドは、PopupWindow の親ビューにトークンがあるかどうかを 50 ミリ秒ごとに 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()
于 2019-08-28T13:25:36.577 に答える
-1

pw.showAtLocationメソッドからステータス バーを考慮して y オフセットを指定できます...

于 2011-02-28T16:16:21.360 に答える