激活Android PopupWindow时背景模糊或暗淡


85

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

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

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


1
警告词:FLAG_BLUR_BEHIND在某些手机上有故障,并使手机反应迟钝(显然模糊是在软件中完成的)。以Droid为例。除此之外,Macarse所说的-FLAG_BLUR_BEHIND需要应用于前景窗口。
EboMike 2010年


1
我正在寻找这个问题的相同答案。我可以通过某种方式使弹出窗口后面的视图变暗吗?
尼克

1
@Nick您如何解决此问题的..
Amit

Answers:


106

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

我找到了一个实际可用于的解决方案Popupwindow。它仅要求您用于后台活动的xml文件的根是FrameLayout。您可以给Framelayout元素android:foreground添加标签。这个标签的作用是指定一个可绘制的资源,该资源将在整个活动之上分层(即,如果Framelayout是xml文件中的根元素)。然后,您可以控制setAlpha()前景可绘制对象的不透明度()。

您可以使用任何喜欢的可绘制资源,但是,如果您只想要调光效果,请在可绘制文件夹中以<shape>标记为根创建xml文件。

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

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

这是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>

这是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);
}

最后,使活动变暗的代码:

layout_MainMenu.getForeground().setAlpha( 220); // dim

layout_MainMenu.getForeground().setAlpha( 0); // restore

Alpha值从0(不透明)变为255(不可见)。当您关闭Popupwindow时,您应该取消对活动的控制。

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


为了确保在旧设备上也能正常工作,您还必须在对其Alpha通道进行每次更改后都使前景无效。我将此添加到答案中。为好答案+1
user2224350 2013年

1
我想不出这个没有被标记为正确答案的原因。。。
Jayshil Dave

2
鉴于操作杆该怎么办,您不能将其

这对我来说效果最好...下面的双重弹出窗口回答具有竞赛条件,因此暗淡的弹出窗口有时会超出实际弹出窗口
kenyee 2014年

1
几乎完美。唯一的缺点是您必须使用FrameLayout,因为其他布局没有“ getForeground”方法。
LiangWang 2015年

79

由于PopupWindow只是增加了一个ViewWindowManager你可以使用updateViewLayout (View view, ViewGroup.LayoutParams params)更新LayoutParams您的PopupWindowcontentView调用演出后..()。

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

请记住,如果您为背景设置背景,PopupWindow则会将其放入contentView容器中,这意味着您需要更新其父级。

有背景:

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

没有背景:

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

棉花糖更新:

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

背景需要更改为以下内容:

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

API 18+的更好替代

使用以下方法的解决方案ViewGroupOverlay

1)保留所需的根目录布局

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

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

2
这似乎不再在运行Android 6.0 Marshmallow的设备上起作用。layoutParams不会强制转换为WindowManager.LayoutParams。有解决方法吗?
罗伯特

谢谢指出。我还没有在M上测试过。将尽快更新。
Markus Rubey 2015年

@ stackoverflow.com/users/308153/robert我也有同样的问题。我在上面的代码中使用了背景。
Rizwan Sohaib 2015年

如果p为空,请使用以下命令:if (p != null) { //same } else { popupView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { v.removeOnLayoutChangeListener(this); WindowManager.LayoutParams p = (WindowManager.LayoutParams) v.getLayoutParams(); p.flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND; p.dimAmount = 0.3f; wm.updateViewLayout(v, p); } }); }
Beytan Kurt

1
仅供参考,当我在较低的API级别(例如15和20)上使用它时,它会崩溃,因为popup.getContentView()。getParent()不会返回View,因此强制转换会导致崩溃。对于这种情况,我使用@BeytanKurt的方法作为后备。
马修·霍斯特

29

在您的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>

在您的活动中

//setting background dim when showing popup
back_dim_layout = (RelativeLayout) findViewById(R.id.share_bac_dim_layout);

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

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

1
请不要在帖子中留下“谢谢,Rupesh”之类的签名。
Simon Hellinger 2013年

3
但这不会使动作/工具栏变暗。
Silvia H

请告诉我如何操作工具栏变暗
SAndroidD

添加back_dim_layout.setVisibility(View.GONE); 在PopupWindow.OnDismissListener中
-Pulkit

肯定帮了我
Pixxu Waku

12

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

创建弹出窗口时的顺序:首先 显示虚拟弹出窗口,然后显示预期的弹出窗口。

销毁时的顺序: 关闭预期的弹出窗口,然后关闭虚拟弹出窗口。

链接这两者的最好方法是添加一个OnDismissListener并重写onDismiss()用于从其弹出虚拟弹出窗口的方法。

虚拟弹出窗口的代码:

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>

显示淡入淡出弹出框以使背景变暗

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

几乎对我有用...有时,在某种竞争条件下,弹出窗口位于暗/虚拟弹出窗口的后面。尚未找到解决方法...甚至延迟popupwindow的显示以让它​​在延迟的窗口后面显示时间也无济于事:-P
kenyee 2014年

2
@kenyee我已经找到了解决此问题的方法。我首先显示“ fade”弹出窗口,然后为了显示第二个弹出窗口,我正在使用myFadePopup.getContentView()。post(... myPopup.showAtLocation ...);
Piotr 2014年

5

我已经找到了解决方案

创建一个自定义透明对话框,然后在该对话框中打开弹出窗口:

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(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" />

现在,您想在“弹出窗口”关闭时关闭对话框。所以

mQuickAction.setOnDismissListener(new OnDismissListener()
{
    @Override
    public void onDismiss()
    {
        if(dialog!=null)
        {
            dialog.dismiss(); // dismiss the empty dialog when the PopupWindow closes
            dialog = null;
        }
    }
});

注意:我在NewQuickAction这里使用了用于创建PopupWindow的插件。也可以在本机Popup Windows上完成


1

对我来说,类似Abdelhak Mouaamou的答案的作品在API级别16和27上进行了测试。

我只是使用已经返回视图的方法popupWindow.getContentView().getParent(),而不是使用并将结果强制转换为View(导致API级别16崩溃,导致返回的ViewRootImpl对象不是对象的实例View.getRootView(),因此不再需要强制转换。

希望它可以帮助某人:)

完整的工作示例是从其他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. /programming/24832497 and /programming/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);
    }
}

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

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


0

您可以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">

这似乎并不能解决问题。我将其应用于我的唯一活动,即当我显示一个弹出窗口时,它是背景,这只是我拥有的放大布局,而不是单独的活动。如何将FLAG_BLUR_BEHIND应用于PopupWindow?
k_day

0

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

所以我尝试过这种方式

创建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>

而作为添加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>

最后,我在弹出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);

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

它对我有用,也解决了BaDo评论的问题。与此同时Actionbar也可以变暗。

附言:我并不是说uhmdown错了。我从他的答案中学到了东西,并尝试着解决我的问题。我也很困惑这是否是一个好方法。

任何建议也表示赞赏,也对我的英语不好对不起。


0

也许这个仓库可以为您提供帮助:BasePopup

这是我的仓库,用于解决PopupWindow的各种问题。

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

有关更多详细信息,请参见Wiki。(zh-cn中的语言)

BasePopup:Wiki


-1

此代码有效

        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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.