安卓弹出窗口的关闭

63

我有一个弹出窗口,当我在我的列表活动中点击一个项目时,它会显示。问题是按返回键并不能关闭它。我尝试在我的列表活动中捕获返回键,但它没有注册...然后我尝试在传递给我的弹出窗口的视图上注册onkeylistener,像这样:

pop.setOnKeyListener(new View.OnKeyListener() {

        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            // TODO Auto-generated method stub
            boolean res=false;
            if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
                // do something on back.
                Log.e("keydown","back");
                if (pw.isShowing()) {
                    Log.e("keydown","pw showing");
                    pw.dismiss();
                    res = true;
                }
            } else {
                res = false;
            }
            return res;
        }
    });

这将传递给弹出窗口,例如:

pw = new PopupWindow(
       pop, 
       240, 
       70, 
       true);

但是那个监听器也没有触发。你能帮我吗?我已经没有想法了 :)


是的,但弹出窗口包含可点击的图像... - Bostjan
11个回答

150

这是因为弹出窗口如果没有背景(background),则无法响应 onTouch 或 onKey 事件。如果您想要解决这个问题,可以参考我编写的代码。在基本情况下,您可以调用 PopupWindow#setBackgroundDrawable(new BitmapDrawable()) 来强制它按照您的期望行事。您无需自己创建 onKey 监听器。如果您希望在用户单击窗口边界外时关闭弹出窗口,还需要调用 PopupWindow#setOutsideTouchable(true)

深入的解释:

背景不能为 null 的原因在于 PopupWindow#preparePopup 函数中的逻辑。如果检测到 background != null,则它会创建一个 PopupViewContainer 实例,并在其中调用 setBackgroundDrawable 并将您的内容视图放入其中。 PopupViewContainer 基本上是一个监听触摸事件和 KeyEvent.KEYCODE_BACK 事件来关闭窗口的 FrameLayout。如果 background==null,则不进行任何操作并直接使用您的内容视图。作为替代方案,您可以扩展根 ViewGroup 以实现所需的行为,而不是依赖 PopupWindow 来处理它。


11
确保在显示对话框之前调用setBackgroundDrawable方法...我花了一段时间才弄清楚这个问题。 - DallinDyer
2
我可以问一下你是怎么发现这个的吗?对我来说,这个是不可能猜到的,所以要么你有某些隐藏文档的访问权限,要么你就是一个巫师 :) - Raffaele
2
@Raffaele Android是开源的,这意味着当您需要更多信息时,您可以随时获取代码并自行挖掘。请参见http://source.android.com/。 - Scott W
3
@ScottW,你真的认为我不知道这个吗?提供来源并不能为编写差劲的文档辩护。当然,我有这些来源,并经常使用调试器来理解为什么事情不能如预期般工作。但这显然是一个Android的故障。即使有了来源和调试器,也很难弄清楚,因为您无法触发问题,因为窗口不响应触摸事件。 - Raffaele
1
@ScottW 没问题 :D 在 Android 上开发非常有趣,但通常开发过程并不像应该的那样直接。 - Raffaele
显示剩余5条评论

39

按照以下方式操作,它可以正常工作:

PopupWindow pw;
LayoutInflater inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.weight_popup, (ViewGroup)findViewById(R.id.linlay_weight_popup));
pw = new PopupWindow(layout,LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT, true);
pw.setBackgroundDrawable(new BitmapDrawable());
pw.setOutsideTouchable(true);
pw.showAsDropDown(btnSelectWeight);

8

对于新项目来说,最好使用:

popupWindow.setBackgroundDrawable(new ColorDrawable());

替代

popupWindow.setBackgroundDrawable(new BitmapDrawable());

由于BitmapDrawable被弃用,因此在这种情况下比ShapeDrawable更好。我注意到当PopupWindow是带圆角的矩形时,ShapeDrawable会将角落填充为黑色。


5

一个非常简单的解决方案是写pw.setFocusable(true),但可能你不想这样做,因为那样MapActivity将无法处理触摸事件。

更好的解决方案是重写返回键,例如像这样:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {

    // Override back button
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        if (pw.isShowing()) {
            pw.dismiss();
            return false;
        }
    }
    return super.onKeyDown(keyCode, event);
} 

祝你好运!


5
对于新手用户,由于创建new BitmapDrawable现在不再被允许(The constructor BitmapDrawable() is deprecated),因此你需要将其更改为new ShapeDrawable(),这样你就需要修改以下代码:
pw.setBackgroundDrawable(new BitmapDrawable());

收件人:

pw.setBackgroundDrawable(new ShapeDrawable());

整个工作流程如下:
PopupWindow pw;
LayoutInflater inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.weight_popup, (ViewGroup)findViewById(R.id.linlay_weight_popup));
pw = new PopupWindow(layout,LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT, true);
pw.setOutsideTouchable(true);
pw.setBackgroundDrawable(new ShapeDrawable());
pw.setTouchInterceptor(new OnTouchListener() { // or whatever you want
        @Override
        public boolean onTouch(View v, MotionEvent event)
        {
            if(event.getAction() == MotionEvent.ACTION_OUTSIDE) // here I want to close the pw when clicking outside it but at all this is just an example of how it works and you can implement the onTouch() or the onKey() you want
            {
               pw.dismiss();
               return true;
            }
            return false;
        }

});
pw.showAtLocation(layout, Gravity.CENTER, 0, 0);

4

只需使用这个

mPopupWindow.setBackgroundDrawable(new BitmapDrawable(null,""));

不建议使用new ShapeDrawable(),因为它会在屏幕需要重新绘制时慢慢地尝试绘制一个形状。相反,推荐使用未被弃用的其他方法。


3
我希望这可以帮助到您。
 pw.setTouchInterceptor(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                pw.dismiss();
            }
            return true;
        }
    });

你好,当我们处理弹出窗口时,这是必须的吗?或者说 pw.setOutsideTouchable(true); 已经足够在我们点击弹出窗口外部时关闭弹出窗口了吗? - Sam YC
@GMsoF 两者都是必须的,看看我的回答。 - Muhammed Refaat

1
你需要为你的PopupWindow添加setBackgroundDrawable(new BitmapDrawable())

0
pw.setBackgroundDrawable(new ColorDrawable());  

必须在setContentView之前编写。

这对我很有效。


0
    private void initPopupWindow() {  
    // TODO Auto-generated method stub  

    View view = getLayoutInflater().inflate(R.layout.main_choice, null);  

    ListView main_menu_listview = (ListView) view.findViewById(R.id.main_menu_listview);  

    ShowMainChoice madapter = new ShowMainChoice(context);
    main_menu_listview.setAdapter(madapter);

    int width = (int)getWindowManager().getDefaultDisplay().getWidth()/2;
    popupWindow = new PopupWindow(view, width,WindowManager.LayoutParams.WRAP_CONTENT);  
    popupWindow.setBackgroundDrawable(new BitmapDrawable());//this is important,如果缺少这句将导致其他任何控件及监听都得不到响应
    popupWindow.setOutsideTouchable(true);
    popupWindow.setFocusable(true);

    main_menu_listview.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {
            // TODO Auto-generated method stub

            Log.e("++++++>", arg2+"");

        }
    });
}

这个问题是由popupwindow底层的消息机制决定的,因为它是阻塞式的。祝你好运。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接