Android popup window dismissal
Asked Answered
H

11

63

I have a popup window displaying when I click an item in my list activity. The problem is that the back key doesn't close it. I tried catching the back key in my list activity but it doesn't register it...then I tried registering a onkeylistener to the view I'm passing to my popup window. Like this:

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

which is passed to a popup like this:

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

But that listener doesn't fire neither. Can you help me? I'm out of ideas :)

Halcyon answered 25/6, 2010 at 20:30 Comment(1)
Yes, but the popup window contains clickable images...Halcyon
D
150

This is because the popup window does not respond to onTouch or onKey events unless it has a background that != null. Check out some code I wrote to help with this. In the basic case you can to call PopupWindow#setBackgroundDrawable(new BitmapDrawable()) to force it to act the way you expect. You won't need your own onKey listener. You might also need to call PopupWindow#setOutsideTouchable(true) if you want it to go away when the user clicks outside of the window boundaries.

Extended esoteric answer:

The reason the background cannot be null is because of what happens in PopupWindow#preparePopup. If it detects background != null it creates an instance of PopupViewContainer and calls setBackgroundDrawable on that and puts your content view in it. PopupViewContainer is basically a FrameLayout that listens for touch events and the KeyEvent.KEYCODE_BACK event to dismiss the window. If background == null, it doesn't do any of that and just uses your content view. You can, as an alternative to depending on PopupWindow to handle that, extend your root ViewGroup to behave the way you want.

Doriandoric answered 26/6, 2010 at 4:24 Comment(10)
I have a very similar problem. My CustomView, which is passed to the PopupWindow doesn't register onKeyDown() events, but it does register the onTouchEvent().Triatomic
make sure you call setBackgroundDrawable before showing the dialog... took me awhile to figure that one out.Eggshaped
May I ask how did you discover this? To me, it's just impossible to guess, so either you have access to some hidden documentation or you are a wizard :)Isleen
@Isleen Android is open source, which means that you can always grab the code and dig into it yourself when you need more information. See source.android.comYawl
@ScottW do you really think I didn't know of it? Giving the sources is not an excuse for writing poor documentation. Sure, I do have the sources and often use my debugger to understand why things don't work as expected. But this is clearly an Android failure. Even with the sources and the debugger, it's difficult to figure this out, because you can't trigger the problem, as the windows doesn't respond to touch eventsIsleen
@Isleen fair enough, the documentation is pretty underwhelming in many places. I must have misread the tone of your question in assuming that you hadn't known about the availability of the source code.Yawl
@ScottW no problem :D developing on Android is much fun, but often the development process doesn't feel as straight as it should doIsleen
Hi do you mean by calling PopupWindow#setOutsideTouchable(true) only will dismiss the Popup Window without doing anything extra? Yes, I have set the background drawable.Guanase
What the hell, they really should mention "In the basic case you must to call PopupWindow#setBackgroundDrawable(new BitmapDrawable()) " this in the API, I have spending whole morning in trying accept that stupid touch event, even I have set the contentview's background drawable to some color.Guanase
setBackgroundDrawable removes the elevation or dropdown shadow. Any ways to keep the elevation?Logbook
A
39

Do as per following it works fine:

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);
Actionable answered 16/11, 2011 at 6:34 Comment(0)
T
8

For new projects it's better to use

popupWindow.setBackgroundDrawable(new ColorDrawable());

instead of

popupWindow.setBackgroundDrawable(new BitmapDrawable());

as BitmapDrawable is deprecated. Also, it's better than ShapeDrawable in this case. I noticed that when PopupWindow is a rectangle with rounded corners, ShapeDrawable fills corners with black.

Thelmathem answered 26/8, 2014 at 14:31 Comment(0)
P
5

A really simple solution is to write pw.setFocusable(true), but probably you don't want to do this because then the MapActivity won't handle touch events.

A better solution is to override the back key, e.g like this:

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

Good luck!

Pentastyle answered 8/11, 2010 at 8:10 Comment(0)
L
5

For the new searchers, as creating a new BitmapDrawable is not allowed now(The constructor BitmapDrawable() is deprecated) , so that you have to change it to a new ShapeDrawable(), so that you will change :

pw.setBackgroundDrawable(new BitmapDrawable());

To :

pw.setBackgroundDrawable(new ShapeDrawable());

And the whole work will be like :

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);
Linder answered 22/5, 2014 at 9:12 Comment(0)
L
4

just use this

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

which is not deprecated. i'd avoid new ShapeDrawable() as its going to render slowly as it tries to draw a shape when the screen needs to be redrawn.

Lenna answered 27/5, 2014 at 2:46 Comment(0)
T
3

I hope this will be help for you

 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;
        }
    });
Taffrail answered 18/11, 2011 at 7:25 Comment(2)
Hi, is this a must when we are dealing with popup window? Or pw.setOutsideTouchable(true); is enough to dismiss the popup window when we click outside of the popup window?Guanase
@GMsoF both are must, look at my answerLinder
T
1

you need add setBackgroundDrawable(new BitmapDrawable()) for your PopupWindow.

Tricornered answered 13/3, 2015 at 2:1 Comment(0)
E
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+"");

        }
    });
}

This problem is popupwindow底层的消息机制决定的,因为它是阻塞式的。Good luck

Eton answered 22/8, 2013 at 2:48 Comment(0)
J
0
pw.setBackgroundDrawable(new ColorDrawable());  

must wrote it before setContentView

This works for me.

Juice answered 28/12, 2018 at 13:39 Comment(0)
U
0

Are you looking for the combination of the popupwindow dismiss and a good working of the BACK button, then you may consider the below solution.

Solution principle: all button clicks near your popup window will be intercepted, but any BACK button will not be intercepted. So, if you have anything in you popupwindow that takes action, then set an indication just before your call to dismiss(). In your setOnDismissListener() perform an extra action (like getActivity().popupBackStack()).

The advantage of this solution is that you can create your own CustomPopupWindow and implement this strategy. You can hide this implementation in your custom popup window.

Step 1: add near to your instantiation of your Popup Window:

boolean isClickHandled = false; 
popupWindow.setOutsideTouchable(true);
popupWindow.setBackgroundDrawable(new ShapeDrawable());
popupWindow.setTouchInterceptor(new View.OnTouchListener() { // or whatever you want
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        isClickHandled = true;
        return false;
    }
});

If you have buttons inside your popupWindow, have the setOnClickListener.onClick set the isClickHandled = true and dismiss().

In your onDismissListener do something like:

popupWindow.setOnDismissListener(() -> {
        popupWindow.dismiss();
        if ( !isClickHandled) {
            MainActivity.mainActivity.getSupportFragmentManager().popBackStack();
        }
    });
Understructure answered 23/5, 2020 at 13:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.