Dim the background using PopupWindow in Android
Asked Answered
N

3

11

Can I dim the background using a PopupWindow instead of a Dialog? I am asking this because I am using Dialogs but the Dialog doesn't show below the item clicked and with the PopupWindow I already have the popup showing below the item.

Nador answered 8/3, 2016 at 17:27 Comment(0)
G
42

I use the following code, and it works well for me.

public static void dimBehind(PopupWindow popupWindow) {
    View container;
    if (popupWindow.getBackground() == null) {
        if (VERSION.SDK_INT >= VERSION_CODES.M){
            container = (View) popupWindow.getContentView().getParent();
        } else {
            container = popupWindow.getContentView();
        }
    } else {
        if (VERSION.SDK_INT >= VERSION_CODES.M) {
            container = (View) popupWindow.getContentView().getParent().getParent();
        } else {
            container = (View) popupWindow.getContentView().getParent();
        }
    }
    Context context = popupWindow.getContentView().getContext();
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    WindowManager.LayoutParams p = (WindowManager.LayoutParams) container.getLayoutParams();
    p.flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND;
    p.dimAmount = 0.3f;
    wm.updateViewLayout(container, p);
}

From this answer.

Update:

The fdermishin's answer below is better. I have tested it backwards to API level 19 and it works well.

If you are using Kotlin, it is better to use it as Kotlin extension:

//Just new a kotlin file(e.g. ComponmentExts),
//copy the following function declaration into it
/**
 * Dim the background when PopupWindow shows
 */
fun PopupWindow.dimBehind() {
    val container = contentView.rootView
    val context = contentView.context
    val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
    val p = container.layoutParams as WindowManager.LayoutParams
    p.flags = p.flags or WindowManager.LayoutParams.FLAG_DIM_BEHIND
    p.dimAmount = 0.3f
    wm.updateViewLayout(container, p)
}

//then use it in other place like this:
popupWindow.dimBehind()

Gerdy answered 19/7, 2016 at 6:18 Comment(3)
View container = (View) popUp.getContentView().getRootView(); This works for pre and post M. Checked in Jelly bean and Marshmallow.Brittain
This crashes in the Nougat simulator: com.android.launcher3 W/OpenGLRenderer: Incorrectly called buildLayer on View: ShortcutAndWidgetContainer, destroying layer.... Anyhow, code just doesn't sit comfortably with me, that variable number of hard-coded getParent()s - doesn't seem future-proof.Selenodont
There is one line that is incorrect. It should be p.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; // add a flag here instead of clear othersDicks
F
29

It seems that I figured out how to get rid of API version checks. Using container = popupWindow.getContentView().getRootView() solves the problem, however I haven't tested it for old APIs yet. Adapting Junyue Cao's solution:

public static void dimBehind(PopupWindow popupWindow) {
    View container = popupWindow.getContentView().getRootView();
    Context context = popupWindow.getContentView().getContext();
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    WindowManager.LayoutParams p = (WindowManager.LayoutParams) container.getLayoutParams();
    p.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
    p.dimAmount = 0.3f;
    wm.updateViewLayout(container, p);
}
Firstrate answered 12/10, 2017 at 13:41 Comment(5)
I like this one. It needs to be called after the window has been shown, and works very well. Adapted as a Kotlin extension on PopupWindow it does wondersOblation
I love this, Added as a kotlin extension tooBedabble
Tested on API level 21 and up to the newest (28) today and it works on all versions.Emelinaemeline
Very nice answer! Tested on API level 16 and it works.Jeep
Fun fact: originally I've implemented this solution in Kotlin in my project, but then converted it to Java to post the answer, because Kotlin was not nearly as popular as Java back thenFirstrate
D
4

JiaJiaGu's solution works but the popupWindow will overlap with navigation bar since it clears all the other flags.

So I change a little bit here:

public static void dimBehind(PopupWindow popupWindow) {
    View container;
    if (VERSION.SDK_INT >= VERSION_CODES.M){
        container = (View) popupWindow.getContentView().getParent();
    } else {
        container = popupWindow.getContentView();
    } 
    if (popupWindow.getBackground() != null) {
        container = container.getParent()
    }
    Context context = popupWindow.getContentView().getContext();
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    WindowManager.LayoutParams p = (WindowManager.LayoutParams) container.getLayoutParams();
    p.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; // add a flag here instead of clear others
    p.dimAmount = 0.3f;
    wm.updateViewLayout(container, p);
}

I post a new answer because I can't comment on JiaJiaGu's answer.

Dicks answered 26/8, 2016 at 2:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.