アプリケーションの初回起動時にのみ表示されるポップアップウィンドウを作成しようとしています。テキストを表示し、ポップアップを閉じるボタンが必要です。ただし、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は無効です」と表示します
私は間違って何をしているのですか?私は非常に初心者なので、我慢してください。
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はこの場合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();
}
受け入れられた答えは私にはうまくいきませんでした。まだBadTokenExceptionを受け取っています。そのため、ハンドラーからRunnableを遅延付きで呼び出しました。
new Handler().postDelayed(new Runnable() {
public void run() {
showPopup();
}
}, 100);
クラスContextを使用します。 getApplicationContext()の代わりにMainActivity.this
この例外が発生する可能性のあるシナリオは2つあります。 1つはkordzikによって言及されています。他のシナリオはここに記載されています: http://blackriver.to/2012/08/Android-annoying-exception-unable-to-add-window-is-your-activity-running/
必ず両方を処理してください
解決策は、スピナーモードを次のようにダイアログに設定することです。
Android:spinnerMode="dialog"
または
Spinner(Context context, int mode)
tnxs RamallahDroid
ユースケースによっては、ポップアップの種類がメッセージを表示する場合、setWindowLayoutType()
を使用してポップアップの種類をTYPE_TOASTに設定すると、この種類のポップアップは基礎となるものに依存しないため、問題を回避できます。アクティビティ。
編集:副作用の1つ:タッチ可能/フォーカス可能イベントがシステムによって削除されるため、API <= 18のポップアップウィンドウでの対話はありません。 ( http://www.jianshu.com/p/634cd056b90c )
最終的にTYPE_PHONEを使用することになります(アプリにSYSTEM_ALERT_WINDOWのアクセス許可が付与されているため、そうでない場合も機能しません)。
ルートビューにトークンがある場合は、ルートビューを確認できます。アクティビティxml、mRootViewから定義された親レイアウトを取得できます。
if (mRootView != null && mRootView.getWindowToken() != null) {
popupWindow.showAtLocation();
}
別のPopupWindowにPopupWindowを表示する場合、最初のPOPでビューを使用せず、Origin親ビューを使用します。
pop.showAtLocation(parentView, ... );
このチェックを使用することもできます:
public void showPopupProgress (){
new Handler().post(new Runnable() {
@Override
public void run() {
if (getWindow().getDecorView().getWindowVisibility() == View.GONE) {
showPopupProgress();
return;
}
popup.showAtLocation(.....);
}
});
}
findViewById
が何かを返すことを確認してください-レイアウトが構築される前に、それを呼び出すのが早すぎるかもしれません
また、取得している例外のlogcat出力を投稿することもできます。
たぶん、新しいソリューションの時間です。このメソッドは、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()
dialog.show()
のAlertDialogで同じ問題(BadTokenException)が発生しました。私はいくつかの例に従ってAlertDialogを作成していました。私の場合、その問題の理由は文字列dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST)
でした
私はそれを削除した後、すべてが動作するようになりました。