Blur or dim background when Android PopupWindow active
Solution 1:
The question was about the Popupwindow
class, yet everybody has given answers that use the Dialog
class. Thats pretty much useless if you need to use the Popupwindow
class, because Popupwindow
doesn't have a getWindow()
method.
I've found a solution that actually works with Popupwindow
. It only requires that the root of the xml file you use for the background activity is a FrameLayout
. You can give the Framelayout
element an android:foreground
tag. What this tag does is specify a drawable resource that will be layered on top of the entire activity (that is, if the Framelayout is the root element in the xml file). You can then control the opacity (setAlpha()
) of the foreground drawable.
You can use any drawable resource you like, but if you just want a dimming effect, create an xml file in the drawable folder with the <shape>
tag as root.
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#000000" />
</shape>
(See http://developer.android.com/guide/topics/resources/drawable-resource.html#Shape for more info on the shape
element).
Note that I didn't specify an alpha value in the color tag that would make the drawable item transparent (e.g #ff000000
). The reason for this is that any hardcoded alpha value seems to override any new alpha values we set via the setAlpha()
in our code, so we don't want that.
However, that means that the drawable item will initially be opaque (solid, non-transparent). So we need to make it transparent in the activity's onCreate()
method.
Here's the Framelayout xml element code:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mainmenu"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:foreground="@drawable/shape_window_dim" >
...
... your activity's content
...
</FrameLayout>
Here's the Activity's onCreate() method:
public void onCreate( Bundle savedInstanceState)
{
super.onCreate( savedInstanceState);
setContentView( R.layout.activity_mainmenu);
//
// Your own Activity initialization code
//
layout_MainMenu = (FrameLayout) findViewById( R.id.mainmenu);
layout_MainMenu.getForeground().setAlpha( 0);
}
Finally, the code to dim the activity:
layout_MainMenu.getForeground().setAlpha( 220); // dim
layout_MainMenu.getForeground().setAlpha( 0); // restore
The alpha values go from 0
(opaque) to 255
(invisible).
You should un-dim the activity when you dismiss the Popupwindow.
I haven't included code for showing and dismissing the Popupwindow, but here's a link to how it can be done: http://www.mobilemancer.com/2011/01/08/popup-window-in-android/
Solution 2:
Since PopupWindow
just adds a View
to WindowManager
you can use updateViewLayout (View view, ViewGroup.LayoutParams params)
to update the LayoutParams
of your PopupWindow
's contentView
after calling show..().
Setting the window flag FLAG_DIM_BEHIND
will dimm everything behind the window. Use dimAmount
to control the amount of dim (1.0 for completely opaque to 0.0 for no dim).
Keep in mind that if you set a background to your PopupWindow
it will put your contentView
into a container, which means you need to update it's parent.
With background:
PopupWindow popup = new PopupWindow(contentView, width, height);
popup.setBackgroundDrawable(background);
popup.showAsDropDown(anchor);
View container = (View) popup.getContentView().getParent();
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams p = (WindowManager.LayoutParams) container.getLayoutParams();
// add flag
p.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
p.dimAmount = 0.3f;
wm.updateViewLayout(container, p);
Without background:
PopupWindow popup = new PopupWindow(contentView, width, height);
popup.setBackgroundDrawable(null);
popup.showAsDropDown(anchor);
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams p = (WindowManager.LayoutParams) contentView.getLayoutParams();
// add flag
p.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
p.dimAmount = 0.3f;
wm.updateViewLayout(contentView, p);
Marshmallow Update:
On M PopupWindow wraps the contentView inside a FrameLayout called mDecorView. If you dig into the PopupWindow source you will find something like createDecorView(View contentView)
.The main purpose of mDecorView is to handle event dispatch and content transitions, which are new to M. This means we need to add one more .getParent() to access the container.
With background that would require a change to something like:
View container = (View) popup.getContentView().getParent().getParent();
Better alternative for API 18+
A less hacky solution using ViewGroupOverlay
:
1) Get a hold of the desired root layout
ViewGroup root = (ViewGroup) getWindow().getDecorView().getRootView();
2) Call applyDim(root, 0.5f);
or clearDim()
public static void applyDim(@NonNull ViewGroup parent, float dimAmount){
Drawable dim = new ColorDrawable(Color.BLACK);
dim.setBounds(0, 0, parent.getWidth(), parent.getHeight());
dim.setAlpha((int) (255 * dimAmount));
ViewGroupOverlay overlay = parent.getOverlay();
overlay.add(dim);
}
public static void clearDim(@NonNull ViewGroup parent) {
ViewGroupOverlay overlay = parent.getOverlay();
overlay.clear();
}
Solution 3:
In your xml file add something like this with width and height as 'match_parent'.
<RelativeLayout
android:id="@+id/bac_dim_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C0000000"
android:visibility="gone" >
</RelativeLayout>
In your activity oncreate
//setting background dim when showing popup
back_dim_layout = (RelativeLayout) findViewById(R.id.share_bac_dim_layout);
Finally make visible when you show your popupwindow and make its visible gone when you exit popupwindow.
back_dim_layout.setVisibility(View.VISIBLE);
back_dim_layout.setVisibility(View.GONE);
Solution 4:
Another trick is to use 2 popup windows instead of one. The 1st popup window will simply be a dummy view with translucent background which provides the dim effect. The 2nd popup window is your intended popup window.
Sequence while creating pop up windows: Show the dummy pop up window 1st and then the intended popup window.
Sequence while destroying: Dismiss the intended pop up window and then the dummy pop up window.
The best way to link these two is to add an OnDismissListener and override the onDismiss()
method of the intended to dimiss the dummy popup window from their.
Code for the dummy popup window:
fadepopup.xml
<?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:orientation="vertical"
android:id="@+id/fadePopup"
android:background="#AA000000">
</LinearLayout>
Show fade popup to dim the background
private PopupWindow dimBackground() {
LayoutInflater inflater = (LayoutInflater) EPGGRIDActivity.this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View layout = inflater.inflate(R.layout.fadepopup,
(ViewGroup) findViewById(R.id.fadePopup));
PopupWindow fadePopup = new PopupWindow(layout, windowWidth, windowHeight, false);
fadePopup.showAtLocation(layout, Gravity.NO_GRAVITY, 0, 0);
return fadePopup;
}
Solution 5:
I've found a solution for this
Create a custom transparent dialog and inside that dialog open the popup window:
dialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar);
emptyDialog = LayoutInflater.from(context).inflate(R.layout.empty, null);
/* blur background*/
WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
lp.dimAmount=0.0f;
dialog.getWindow().setAttributes(lp);
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
dialog.setContentView(emptyDialog);
dialog.setCanceledOnTouchOutside(true);
dialog.setOnShowListener(new OnShowListener()
{
@Override
public void onShow(DialogInterface dialogIx)
{
mQuickAction.show(emptyDialog); //open the PopupWindow here
}
});
dialog.show();
xml for the dialog(R.layout.empty):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent" android:layout_width="match_parent"
style="@android:style/Theme.Translucent.NoTitleBar" />
now you want to dismiss the dialog when Popup window dismisses. so
mQuickAction.setOnDismissListener(new OnDismissListener()
{
@Override
public void onDismiss()
{
if(dialog!=null)
{
dialog.dismiss(); // dismiss the empty dialog when the PopupWindow closes
dialog = null;
}
}
});
Note: I've used NewQuickAction
plugin for creating PopupWindow here. It can also be done on native Popup Windows