Android popup window dismissal
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 somethin开发者_StackOverflow社区g 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 :)
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.
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);
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.
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!
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);
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.
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;
}
});
you need add setBackgroundDrawable(new BitmapDrawable())
for your PopupWindow
.
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
pw.setBackgroundDrawable(new ColorDrawable());
must wrote it before setContentView
This works for me.
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();
}
});
精彩评论