简体   繁体   English

Android PopupWindow 活动时模糊或暗淡的背景

[英]Blur or dim background when Android PopupWindow active

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.dismiss is called.当我使用popup.showAtLocation显示我的弹出窗口 window 时,我希望能够模糊或调暗背景,并在调用popup.dismiss时取消模糊/调暗背景。

I have tried applying layout params FLAG_BLUR_BEHIND and FLAG_DIM_BEHIND to my activity, but this appears to just blur and dim the background as soon my app is started.我已经尝试将布局参数FLAG_BLUR_BEHINDFLAG_DIM_BEHIND到我的活动中,但这似乎只是在我的应用程序启动后使背景模糊和变暗。

How can I do blurring/dimming just with popups?我怎样才能用弹出窗口做模糊/变暗?

The question was about the Popupwindow class, yet everybody has given answers that use the Dialog class.问题是关于Popupwindow类,但每个人都给出了使用Dialog类的答案。 Thats pretty much useless if you need to use the Popupwindow class, because Popupwindow doesn't have a getWindow() method.如果您需要使用Popupwindow类,那几乎没有用,因为Popupwindow没有getWindow()方法。

I've found a solution that actually works with Popupwindow .我找到了一个实际适用于Popupwindow的解决方案。 It only requires that the root of the xml file you use for the background activity is a FrameLayout .它只需要您用于后台活动的 xml 文件的根目录是FrameLayout You can give the Framelayout element an android:foreground tag.您可以给Framelayout元素一个android:foreground标签。 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).该标签的作用是指定一个可绘制资源,该资源将位于整个活动的顶部(即,如果 Framelayout 是 xml 文件中的根元素)。 You can then control the opacity ( setAlpha() ) of the foreground drawable.然后,您可以控制前景可绘制对象的不透明度( 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.您可以使用任何您喜欢的可绘制资源,但如果您只想要变暗效果,请在可绘制文件夹中创建一个 xml 文件,并以<shape>标签为根。

<?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). (有关shape元素的更多信息,请参阅http://developer.android.com/guide/topics/resources/drawable-resource.html#Shape )。 Note that I didn't specify an alpha value in the color tag that would make the drawable item transparent (eg #ff000000 ).请注意,我没有在颜色标签中指定使可绘制项目透明的 alpha 值(例如#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.这样做的原因是任何硬编码的 alpha 值似乎都会覆盖我们通过setAlpha()在我们的代码中设置的任何新的 alpha 值,所以我们不希望那样。 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.所以我们需要在活动的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). alpha 值从0 (不透明)到255 (不可见)。 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/我没有包含用于显示和关闭 Popupwindow 的代码,但这里有一个如何完成的链接:http ://www.mobilemancer.com/2011/01/08/popup-window-in-android/

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..().由于PopupWindow只是向WindowManager添加一个View ,因此您可以使用updateViewLayout (View view, ViewGroup.LayoutParams params)在调用 show..() 后更新您的PopupWindowcontentViewLayoutParams

Setting the window flag FLAG_DIM_BEHIND will dimm everything behind the window.设置窗口标志FLAG_DIM_BEHIND将使窗口后面的所有内容FLAG_DIM_BEHIND Use dimAmount to control the amount of dim (1.0 for completely opaque to 0.0 for no dim).使用dimAmount控制暗淡的量(1.0 表示完全不透明,0.0 表示无暗淡)。

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.请记住,如果您为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.在 M PopupWindow 上,将 contentView 包装在一个名为 mDecorView 的 FrameLayout 中。 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.如果你深入研究 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 :使用ViewGroupOverlay不那么笨拙的解决方案:

1) Get a hold of the desired root layout 1) 获取所需的根布局

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

2) Call applyDim(root, 0.5f); 2) 调用applyDim(root, 0.5f); or clearDim()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();
}

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

Another trick is to use 2 popup windows instead of one.另一个技巧是使用 2 个弹出窗口而不是一个。 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.链接这两者的最佳方法是添加一个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;
}

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 NewQuickAction plugin for creating PopupWindow here.注意:我在这里使用了NewQuickAction插件来创建 PopupWindow。 It can also be done on native Popup Windows它也可以在本机弹出窗口上完成

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 ViewRootImpl object 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 上崩溃,导致它返回一个不是View实例的ViewRootImpl对象)我只使用.getRootView()它返回已经有一个视图,所以不需要在那里进行铸造。

Hope it helps someone :)希望它可以帮助某人:)

complete working example scrambled together from other stackoverflow posts, just copy-paste it, eg, 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);
    }
}
findViewById(R.id.drawer_layout).setAlpha((float) 0.7);

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

You can use android:theme="@android:style/Theme.Dialog" to do that.您可以使用android:theme="@android:style/Theme.Dialog"来做到这一点。 Create an activity and in your AndroidManifest.xml define the activity as:创建一个活动并在您的AndroidManifest.xml中将活动定义为:

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

ok, so i follow uhmdown's answer for dimming background activity when pop window is open.好的,所以我按照uhmdown 的答案在弹出窗口打开时调背景活动。 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).它是变暗的活动并包括弹出窗口(意味着在活动和弹出窗口上也分层变暗,不能将它们分开)。

so i tried this way,所以我尝试了这种方式,

create an dimming_black.xml file 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 background in FrameLayout as root xml tag, also put my other controls in LinearLayout like this layout.xml并在FrameLayout添加为background作为根 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 MainActivity with 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:结果: 在此处输入图片说明

it works for me, also solved problem as commented by BaDo .它对我有用,也解决了BaDo评论的问题 With this Actionbar also can be dimmed.使用此操作Actionbar也可以变暗。

Ps i am not saying uhmdown's is wrong. Ps 我并不是说uhmdown是错误的。 i learnt form his answer and try to evolve for my problem.我从他的回答中学习并尝试针对我的问题进行改进。 I also confused whether this is a good way or not.我也很困惑这是否是一个好方法。

Any suggestions is also appreciated also sorry for my bad English.任何建议也很感激,也很抱歉我的英语不好。

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

Since You are trying to pop up your dialog window by blurring the background screen, You must use this set of lines.由于您试图通过模糊背景屏幕来弹出对话框 window,因此您必须使用这组线条。 You need to fetch the dialog attributes first, then set up some alpha values for the dialog attributes.您需要先获取对话框属性,然后为对话框属性设置一些 alpha 值。

Now, your dialog with blur background is ready.现在,您的模糊背景对话框已准备就绪。 But the important factor is to set a Flag FLAG_DIM_BEHIND for the window.但重要的因素是为window设置一个Flag FLAG_DIM_BEHIND

Now the result is yours.现在结果是你的。 Hope it will helpful for someone...希望它会对某人有所帮助...

            WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
            lp.dimAmount=0.6f;
            dialog.getWindow().setAttributes(lp);
            dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

This code work此代码工作

        pwindo = new PopupWindow(layout, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
        pwindo.showAtLocation(layout, Gravity.CENTER, 0, 0);
        pwindo.setOutsideTouchable(false);

        View container = (View) pwindo.getContentView().getParent();
        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;
        wm.updateViewLayout(container, p);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM