今天在帮同学实现一个PopupWindow嵌套PopupWindow时报了异常,导致第二个POP不能显示:
android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRootImpl$W@4340e618 is not valid; is your activity running? |
先贴代码:
volleytest_lay.xml,pop_first_lay.xml,pop_second_lay:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" android:orientation="vertical" > <Button android:id="@+id/popBtn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="弹出POP菜单" /> </LinearLayout> |
PopupWindowActivity.java :
public class PopupWindowActivity extends Activity { private Button popBtn = null, mButton; private Context mContext; private PopupWindow mSecondPOPWindow, mFirstPOPWindow = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.volleytest_lay); mContext = this; popBtn = (Button) findViewById(R.id.popBtn); initPopFirst(); initListener(); } private void initListener() { // TODO Auto-generated method stub popBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub if (mFirstPOPWindow.isShowing()) { // 隐藏窗口,如果设置了点击窗口外小时即不需要此方式隐藏 mFirstPOPWindow.dismiss(); } else { // 显示窗口 mFirstPOPWindow.showAsDropDown(v); } } }); } /** * 初始化第一个POP */ private void initPopFirst() { View firstView = getLayoutInflater().inflate(R.layout.pop_first_lay, null); mFirstPOPWindow = new PopupWindow(firstView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, true); mFirstPOPWindow.setTouchable(true); mFirstPOPWindow.setOutsideTouchable(true); mFirstPOPWindow.setFocusable(true); mFirstPOPWindow.setBackgroundDrawable(new BitmapDrawable()); initPopSecond(); Button btn = (Button) firstView.findViewById(R.id.popFirstBtn); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { if (mSecondPOPWindow.isShowing()) { // 隐藏窗口,如果设置了点击窗口外小时即不需要此方式隐藏 mSecondPOPWindow.dismiss(); } else { // 显示窗口 mSecondPOPWindow.showAsDropDown(v, 500, 500); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } /*//测试AlertDialog new AlertDialog.Builder(mContext) .setTitle("确认") .setMessage("确定吗?") .setPositiveButton("是", null) .setNegativeButton("否", null) .show();*/ } }); } /** * 初始化第二个POP */ private void initPopSecond() { View popSecView = PopupWindowActivity.this.getLayoutInflater().inflate( R.layout.pop_second_lay, null); mSecondPOPWindow = new PopupWindow(popSecView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, true); mSecondPOPWindow.setTouchable(true); mSecondPOPWindow.setOutsideTouchable(true); mSecondPOPWindow.setFocusable(true); mSecondPOPWindow.setBackgroundDrawable(new BitmapDrawable()); Button popSecondBtn = (Button) popSecView .findViewById(R.id.popSecondBtn); popSecondBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Toast.makeText(PopupWindowActivity.this, "第二个POP", 100).show(); } }); } } |
此时是报异常的,经过不断的排查和网上搜查,问题依然没有解决。比如这里http://stackoverflow.com/questions/8782250/popupwindow-badtokenexception-unable-to-add-window-token-null-is-not-valid 只是提出讨论并么有实际解决出来。
异常的原因是因为第一个PopupWindow已经显示后,他就控制了整个Window的焦点,此时第一个POP则成为了父Window,而PopupWindow是不能以子window的形式展现的,他们必须都要以父Window显示,所以第二个POP无法添加上去。
修改后:
public class PopupWindowActivity extends Activity { private Button popBtn = null, mButton; private Context mContext; private PopupWindow mSecondPOPWindow, mFirstPOPWindow = null; private View parientView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.volleytest_lay); mContext = this; popBtn = (Button) findViewById(R.id.popBtn); initPopFirst(); initListener(); } private void initListener() { // TODO Auto-generated method stub popBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub if (mFirstPOPWindow.isShowing()) { // 隐藏窗口,如果设置了点击窗口外小时即不需要此方式隐藏 mFirstPOPWindow.dismiss(); } else { // 显示窗口 mFirstPOPWindow.showAsDropDown(v); } // 给第二个POP显示时用,解决了嵌套时出现的Unable to add window的问题 parientView = v; } }); } /** * 初始化第一个POP */ private void initPopFirst() { View firstView = getLayoutInflater().inflate(R.layout.pop_first_lay, null); mFirstPOPWindow = new PopupWindow(firstView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, true); mFirstPOPWindow.setTouchable(true); mFirstPOPWindow.setOutsideTouchable(true); mFirstPOPWindow.setFocusable(true); mFirstPOPWindow.setBackgroundDrawable(new BitmapDrawable()); initPopSecond(); Button btn = (Button) firstView.findViewById(R.id.popFirstBtn); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { if (mSecondPOPWindow.isShowing()) { // 隐藏窗口,如果设置了点击窗口外小时即不需要此方式隐藏 mSecondPOPWindow.dismiss(); } else { // 显示窗口 mSecondPOPWindow.showAsDropDown(parientView, 500, 500); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } /*//测试AlertDialog new AlertDialog.Builder(mContext) .setTitle("确认") .setMessage("确定吗?") .setPositiveButton("是", null) .setNegativeButton("否", null) .show();*/ } }); } /** * 初始化第二个POP */ private void initPopSecond() { View popSecView = PopupWindowActivity.this.getLayoutInflater().inflate( R.layout.pop_second_lay, null); mSecondPOPWindow = new PopupWindow(popSecView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, true); mSecondPOPWindow.setTouchable(true); mSecondPOPWindow.setOutsideTouchable(true); mSecondPOPWindow.setFocusable(true); mSecondPOPWindow.setBackgroundDrawable(new BitmapDrawable()); Button popSecondBtn = (Button) popSecView .findViewById(R.id.popSecondBtn); popSecondBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Toast.makeText(PopupWindowActivity.this, "第二个POP", 100).show(); } }); } } |
解决方式请看红色标出的部分,而且解决的方式相当简单(此方案得益于一个同学),有时候我们把问题想得太复杂了,反而会使我们陷入一定的僵局,换一种思路去看待问题,必然会柳暗花明。
其实这个问题可以换一种方式解决,那就是第二个POP用AlertDialog显示也是可以,看代码中我注释点的几句就会明白,只是你非要用POP时就用我提供的这个就可以了,当然还有可能有更多的解决方案,如果有其他解决方案时,请分享一起学习,写此博文,只是希望对目前还没解决的人提供一点帮助。