我正在尝试创建一个仅在应用程序第一次启动时出现的弹出窗口。我希望它显示一些文本并有一个按钮来关闭弹出窗口。但是,我在让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无效”
世界上我做错了什么?我是非常新手所以请耐心等待。
为了避免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);
}
});
您也可以尝试使用此检查:
public void showPopupProgress (){
new Handler().post(new Runnable() {
@Override
public void run() {
if (getWindow().getDecorView().getWindowVisibility() == View.GONE) {
showPopupProgress();
return;
}
popup.showAtLocation(.....);
}
});
}
如果在另一个PopupWindow中显示PopupWindow,请不要在第一个POP中使用该视图,请使用原始父视图。
pop.showAtLocation(parentView, ... );
我在dialog.show()
上使用AlertDialog遇到了同样的问题(BadTokenException)。我按照一些例子制作了一个AlertDialog。在我的情况下,这个问题的原因是一个字符串dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST)
我删除它后,一切都变得有效了。
您可以从pw.showAtLocation方法指定y偏移量以考虑状态栏...
如果您连续启动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();
}
接受的答案对我不起作用。我仍然收到BadTokenException。所以我只是从处理程序中调用Runnable,延迟如下:
new Handler().postDelayed(new Runnable() {
public void run() {
showPopup();
}
}, 100);
使用类上下文例如。 MainActivity.this而不是getApplicationContext()
发生此异常时有两种情况。 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可以避免此问题,因为此类型的弹出窗口不依赖于基础活动。
编辑:副作用之一:API <= 18的弹出窗口中没有交互,因为系统会删除可触摸/可聚焦事件。 (http://www.jianshu.com/p/634cd056b90c)
我最终使用TYPE_PHONE(因为应用程序恰好具有SYSTEM_ALERT_WINDOW权限,否则这也不会起作用)。
您可以检查rootview是否有令牌。您可以从活动xml,mRootView中获取定义的父布局
if (mRootView != null && mRootView.getWindowToken() != null) {
popupWindow.showAtLocation();
}
检查findViewById
是否会返回一些内容 - 在构建布局之前,您可能会过早地调用它
此外,您可能希望发布logcat输出以获取您获得的异常