Android PopupWindow 活动时背景模糊或变暗

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3221488/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 09:12:49  来源:igfitidea点击:

Blur or dim background when Android PopupWindow active

androidpopupwindowandroid-popupwindow

提问by k_day

I would like to be able to either blur or dim the background when I show my popup window using popup.showAtLocation, and unblur/dim the background when popup.dismissis called.

我希望能够在使用 显示弹出窗口时使背景模糊或变暗popup.showAtLocation,并在popup.dismiss调用时取消模糊/变暗背景。

I have tried applying layout params FLAG_BLUR_BEHINDand FLAG_DIM_BEHINDto my activity, but this appears to just blur and dim the background as soon my app is started.

我曾尝试应用布局PARAMSFLAG_BLUR_BEHINDFLAG_DIM_BEHIND我的活动,但是这似乎只是模糊和朦胧的背景,一旦我的应用程序已启动。

How can I do blurring/dimming just with popups?

如何仅使用弹出窗口进行模糊/变暗?

回答by uhmdown

The question was about the Popupwindowclass, yet everybody has given answers that use the Dialogclass. Thats pretty much useless if you need to use the Popupwindowclass, because Popupwindowdoesn't have a getWindow()method.

问题是关于Popupwindow班级的,但每个人都给出了使用Dialog班级的答案。如果您需要使用Popupwindow该类,那几乎没有用,因为Popupwindow它没有getWindow()方法。

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 Framelayoutelement an android:foregroundtag. 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.

我找到了一个实际适用于Popupwindow. 它只需要您用于后台活动的 xml 文件的根目录是FrameLayout. 你可以给Framelayout元素一个android:foreground标签。该标签的作用是指定一个可绘制资源,该资源将位于整个活动的顶部(即,如果 Framelayout 是 xml 文件中的根元素)。然后,您可以控制setAlpha()前景可绘制对象的不透明度 ( )。

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.

你可以使用任何你喜欢的 drawable 资源,但如果你只是想要一个变暗的效果,在 drawable 文件夹中创建一个 xml 文件,<shape>标签为 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#Shapefor more info on the shapeelement). 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.

(有关该元素的更多信息,请参阅http://developer.android.com/guide/topics/resources/drawable-resource.html#Shapeshape)。请注意,我没有在颜色标签中指定使可绘制项目透明的 alpha 值(例如 #ff000000)。这样做的原因是任何硬编码的 alpha 值似乎都会覆盖我们通过setAlpha()代码中设置的任何新的 alpha 值,所以我们不希望那样。但是,这意味着可绘制项目最初是不透明的(实心的、不透明的)。所以我们需要在活动的onCreate()方法中让它透明。

Here's the Framelayout xml element code:

这是 Framelayout xml 元素代码:

<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:

这是 Activity 的 onCreate() 方法:

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.

alpha 值从0(不透明)到255(不可见)。当您关闭弹出窗口时,您应该取消活动的亮度。

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/

我没有包含用于显示和关闭 Popupwindow 的代码,但这里有一个如何完成的链接:http: //www.mobilemancer.com/2011/01/08/popup-window-in-android/

回答by Markus Rubey

Since PopupWindowjust adds a Viewto WindowManageryou can use updateViewLayout (View view, ViewGroup.LayoutParams params)to update the LayoutParamsof your PopupWindow's contentViewafter calling show..().

由于PopupWindow只是添加了一个ViewtoWindowManager你可以用来在调用 show..() 后updateViewLayout (View view, ViewGroup.LayoutParams params)更新LayoutParams你的PopupWindow's contentView

Setting the window flag FLAG_DIM_BEHINDwill dimm everything behind the window. Use dimAmountto control the amount of dim (1.0 for completely opaque to 0.0 for no dim).

设置窗口标志FLAG_DIM_BEHIND将使窗口后面的所有内容变暗。使用dimAmount以控制暗淡的(1.0完全不透明到0.0为无暗淡)的量。

Keep in mind that if you set a background to your PopupWindowit will put your contentViewinto a container, which means you need to update it's parent.

请记住,如果您为您设置背景,PopupWindow它将把您contentView放入一个容器中,这意味着您需要更新它的父级。

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.

在 M PopupWindow 上,将 contentView 包装在一个名为 mDecorView 的 FrameLayout 中。如果你深入研究 PopupWindow 源代码,你会发现类似createDecorView(View contentView).mDecorView 的主要目的是处理事件调度和内容转换,这对 M 来说是新的。这意味着我们需要再添加一个 .getParent() 来访问容器。

With background that would require a change to something like:

背景需要更改为:

View container = (View) popup.getContentView().getParent().getParent();


Better alternative for API 18+

API 18+ 的更好选择

A less hacky solution using ViewGroupOverlay:

一个不那么hacky的解决方案使用ViewGroupOverlay

1) Get a hold of the desired root layout

1) 获取所需的根布局

ViewGroup root = (ViewGroup) getWindow().getDecorView().getRootView();

2) Call applyDim(root, 0.5f);or clearDim()

2) 打电话applyDim(root, 0.5f);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();
}

回答by user2257590

In your xml file add something like this with width and height as 'match_parent'.

在您的 xml 文件中添加类似这样的内容,宽度和高度为“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

在你的活动 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.

最后在显示 popupwindow 时使其可见,并在退出 popupwindow 时使其可见。

back_dim_layout.setVisibility(View.VISIBLE);
back_dim_layout.setVisibility(View.GONE);

回答by Abhishek Nandi

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.

另一个技巧是使用 2 个弹出窗口而不是一个。第一个弹出窗口将只是一个带有半透明背景的虚拟视图,提供昏暗的效果。第二个弹出窗口是您想要的弹出窗口。

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 OnDismissListenerand override the onDismiss()method of the intended to dimiss the dummy popup window from their.

链接这两者的最佳方法是添加一个OnDismissListener并覆盖onDismiss()旨在从它们中关闭虚拟弹出窗口的方法。

Code for the dummy popup window:

虚拟弹出窗口的代码:

fadepopup.xml

弹出窗口.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;
}

回答by PC.

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(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 NewQuickActionplugin for creating PopupWindow here. It can also be done on native Popup Windows

注意:我在NewQuickAction这里使用插件来创建 PopupWindow。它也可以在本机弹出窗口上完成

回答by chrisi1698

For me, something like Abdelhak Mouaamou's answer works, tested on API level 16 and 27.

对我来说,像 Abdelhak Mouaamou 的答案一样有效,在 API 级别 16 和 27 上进行了测试。

Instead of using popupWindow.getContentView().getParent()and casting the result to View(which crashes on API level 16 cause there it returns a ViewRootImplobject which isn't an instance of View) I just use .getRootView()which returns a view already, so no casting required there.

而不是使用popupWindow.getContentView().getParent()并将结果转换为View(它在 API 级别 16 上崩溃,导致它返回一个ViewRootImpl不是 的实例的对象View)我只是使用.getRootView()它已经返回一个视图,因此不需要在那里进行转换。

Hope it helps someone :)

希望它可以帮助某人:)

complete working example scrambled together from other stackoverflow posts, just copy-paste it, e.g., in the onClick listener of a button:

从其他 stackoverflow 帖子中拼凑而成的完整工作示例,只需复制粘贴它,例如,在按钮的 onClick 侦听器中:

// inflate the layout of the popup window
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
if(inflater == null) {
    return;
}
//View popupView = inflater.inflate(R.layout.my_popup_layout, null); // this version gives a warning cause it doesn't like null as argument for the viewRoot, c.f. https://stackoverflow.com/questions/24832497 and https://stackoverflow.com/questions/26404951
View popupView = View.inflate(MyParentActivity.this, R.layout.my_popup_layout, null);

// create the popup window
final PopupWindow popupWindow = new PopupWindow(popupView,
        LinearLayout.LayoutParams.WRAP_CONTENT,
        LinearLayout.LayoutParams.WRAP_CONTENT,
        true // lets taps outside the popup also dismiss it
        );

// do something with the stuff in your popup layout, e.g.:
//((TextView)popupView.findViewById(R.id.textview_popup_helloworld))
//      .setText("hello stackoverflow");

// dismiss the popup window when touched
popupView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            popupWindow.dismiss();
            return true;
        }
});

// show the popup window
// which view you pass in doesn't matter, it is only used for the window token
popupWindow.showAtLocation(view, Gravity.CENTER, 0, 0);
//popupWindow.setOutsideTouchable(false); // doesn't seem to change anything for me

View container = popupWindow.getContentView().getRootView();
if(container != null) {
    WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
    WindowManager.LayoutParams p = (WindowManager.LayoutParams)container.getLayoutParams();
    p.flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND;
    p.dimAmount = 0.3f;
    if(wm != null) {
        wm.updateViewLayout(container, p);
    }
}

回答by razerdp

Maybe this repo will help for you:BasePopup

也许这个 repo 对你有帮助:BasePopup

This is my repo, which is used to solve various problems of PopupWindow.

这是我的repo,用来解决PopupWindow的各种问题。

In the case of using the library, if you need to blur the background, just call setBlurBackgroundEnable(true).

在使用库的情况下,如果需要模糊背景,只需调用 setBlurBackgroundEnable(true).

See the wiki for more details.(Language in zh-cn)

详见wiki。(zh-cn中的语言)

BasePopup:wiki

基本弹出窗口:wiki

回答by Devbrat Anuragi

findViewById(R.id.drawer_layout).setAlpha((float) 0.7);

R.id.drawer_layoutis the id of the layout of which you want to dim the brightness.

R.id.drawer_layout是要调暗亮度的布局的 id。

回答by Sahil M.

ok, so i follow uhmdown'sanswer for dimming background activity when pop window is open. But it creates problem for me. it was dimming activity and include popup window (means dimmed-black layered on both activity and popup also, it can not be separate them).

好的,所以我按照uhmdown 的答案在弹出窗口打开时调背景活动。但它给我带来了问题。它是变暗的活动并包括弹出窗口(意味着在活动和弹出窗口上也有变暗的黑色分层,它不能将它们分开)。

so i tried this way,

所以我尝试了这种方式,

create an dimming_black.xmlfile for dimming effect,

创建一个dimming_black.xml文件用于调光效果,

 <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#33000000" />
</shape>

And add as backgroundin FrameLayoutas root xml tag, also put my other controls in LinearLayoutlike this layout.xml

而作为添加backgroundFrameLayout为根XML标记,也把我的其他控件LinearLayout像这样layout.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@drawable/ff_drawable_black">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="bottom"
        android:background="@color/white">

        // other codes...
    </LinearLayout>

</FrameLayout>

at last i show popup on my MainActivitywith some extra parameter set as below.

最后我在我的弹出窗口中显示MainActivity了一些额外的参数设置,如下所示。

           //instantiate popup window
            popupWindow = new PopupWindow(viewPopup, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, true);

            //display the popup window
            popupWindow.showAtLocation(layout_ff, Gravity.BOTTOM, 0, 0);

Result:enter image description here

结果:在此处输入图片说明

it works for me, also solved problem as commented by BaDo. With this Actionbaralso can be dimmed.

它对我有用,也解决了BaDo评论的问题。有了这个Actionbar也可以变暗。

P.s i am not saying uhmdown'sis wrong. i learnt form his answer and try to evolve for my problem. I also confused whether this is a good way or not.

Ps 我并不是说uhmdown是错误的。我从他的答案中学习并尝试针对我的问题进行改进。我也很困惑这是否是一个好方法。

Any suggestions is also appreciated also sorry for my bad English.

任何建议也很感激,也很抱歉我的英语不好。

回答by Macarse

You can use android:theme="@android:style/Theme.Dialog"to do that. Create an activity and in your AndroidManifest.xmldefine the activity as:

你可以用它android:theme="@android:style/Theme.Dialog"来做到这一点。创建一个活动并在您AndroidManifest.xml定义活动为:

    <activity android:name=".activities.YourActivity"
              android:label="@string/your_activity_label"
              android:theme="@android:style/Theme.Dialog">