在Android Activity中创建弹出窗口时出现问题

时间:2010-11-15 18:41:10

标签: android popupwindow

我正在尝试创建一个仅在应用程序第一次启动时出现的弹出窗口。我希望它显示一些文本并有一个按钮来关闭弹出窗口。但是,我在让PopupWindow工作时遇到了麻烦。我尝试了两种不同的方法:

首先,我有一个XML文件,它声明了popup.xml的布局(在linearlayout中的textview),我在主Activity的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无效”

世界上我做错了什么?我非常新手所以请耐心等待。

14 个答案:

答案 0 :(得分:174)

为了避免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);
   }
});

答案 1 :(得分:28)

如果您连续启动2项活动,Kordzik提供的解决方案将无效:

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

答案 2 :(得分:17)

接受的答案对我不起作用。我仍然收到BadTokenException。所以我只是从处理程序中调用了Runnable,因此延迟了:

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

答案 3 :(得分:6)

使用类上下文例如。 MainActivity.this而不是getApplicationContext()

答案 4 :(得分:4)

发生此异常时有两种情况。 kordzik提到了一个。这里提到了其他方案:http://blackriver.to/2012/08/android-annoying-exception-unable-to-add-window-is-your-activity-running/

确保你同时处理它们

答案 5 :(得分:3)

解决方案是将微调器模式设置为对话框,如下所示:

android:spinnerMode="dialog"

Spinner(Context context, int mode)
tnxs RamallahDroid

See This.

答案 6 :(得分:1)

根据用例,对于显示消息的弹出窗口类型,使用setWindowLayoutType()将弹出窗口类型设置为TYPE_TOAST可以避免此问题,因为此类型的弹出窗口不依赖于潜在的活动。

编辑:副作用之一:API< = 18的弹出窗口中没有交互,因为系统会删除可触摸/可聚焦事件。 (http://www.jianshu.com/p/634cd056b90c

我最终使用TYPE_PHONE(因为应用程序碰巧拥有SYSTEM_ALERT_WINDOW权限,否则这也不会起作用)。

答案 7 :(得分:1)

如果拥有令牌,您可以检查rootview。您可以从活动xml,mRootView

中获取父布局
if (mRootView != null && mRootView.getWindowToken() != null) {
    popupWindow.showAtLocation();
}

答案 8 :(得分:0)

检查findViewById是否返回了某些内容 - 在构建布局之前,您可能会过早地调用它

此外,您可能希望发布logcat输出以获取您正在获取的例外

答案 9 :(得分:0)

您也可以尝试使用此检查:

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

答案 10 :(得分:0)

如果在另一个PopupWindow中显示PopupWindow,请不要在第一个POP中使用该视图,请使用原始父视图。

pop.showAtLocation(parentView, ... );

答案 11 :(得分:0)

我在dialog.show()上使用AlertDialog遇到了同样的问题(BadTokenException)。我按照一些例子制作了一个AlertDialog。在我的情况下,这个问题的原因是一个字符串  dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST)

删除后,一切都变得有效了。

答案 12 :(得分: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()

答案 13 :(得分:-1)

您可以从 pw.showAtLocation 方法指定状态栏的y偏移量来计算...