【问题标题】:Blur or dim background when Android PopupWindow activeAndroid PopupWindow 活动时背景模糊或变暗
【发布时间】:2011-03-14 09:13:17
【问题描述】:

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

我已尝试将布局参数 FLAG_BLUR_BEHINDFLAG_DIM_BEHIND 应用于我的活动,但这似乎只是在我的应用启动后使背景变得模糊和变暗。

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

【问题讨论】:

  • 警告词:FLAG_BLUR_BEHIND 在某些手机上存在问题,使手机非常无响应(显然模糊是在软件中完成的)。例如,在 Droid 上。除此之外,Macarse 所说的 - FLAG_BLUR_BEHIND 需要应用于前台窗口。
  • 我正在寻找这个问题的相同答案。有什么方法可以通过编程方式使弹出窗口后面的视图变暗?
  • @Nick 你是如何解决这个问题的..
  • 2021 完美解决方案:github.com/sergei-lapin/BlurView

标签: android popupwindow android-popupwindow


【解决方案1】:

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

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

你可以使用任何你喜欢的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>

(有关shape 元素的更多信息,请参阅http://developer.android.com/guide/topics/resources/drawable-resource.html#Shape)。 请注意,我没有在颜色标签中指定使可绘制项目透明的 alpha 值(例如#ff000000)。原因是任何硬编码的 alpha 值似乎都会覆盖我们通过代码中的 setAlpha() 设置的任何新 alpha 值,所以我们不希望这样。 但是,这意味着可绘制项目最初是不透明的(实心的,不透明的)。所以我们需要在activity的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 的好答案
  • 我想不出这个没有被标记为正确答案的原因.. 至少可以说很棒的答案。
  • 视图中有Actionbar怎么办,你不能调暗它
  • 这对我来说效果最好......下面的双弹出答案有一个竞争条件,所以变暗的弹出窗口有时会超过真正的弹出窗口
  • 几乎完美。唯一的缺点是您必须使用 FrameLayout,因为其他布局没有“getForeground”方法。
【解决方案2】:

由于PopupWindow 只是将View 添加到WindowManager,您可以在调用show..() 后使用updateViewLayout (View view, ViewGroup.LayoutParams params) 更新PopupWindowcontentViewLayoutParams

设置窗口标志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);

棉花糖更新:

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

【讨论】:

  • 这似乎不再适用于运行 Android 6.0 Marshmallow 的设备。 layoutParams 不会转换为 WindowManager.LayoutParams。有解决办法吗?
  • 感谢您的指出。我还没有在M上测试过。将尽快更新。
  • @stackoverflow.com/users/308153/robert 我也有同样的问题。我使用了上面的带有背景的代码。
  • 如果 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); } }); }
  • 仅供参考,当我在较低的 API 级别(例如 15 和 20)上使用它时,它会崩溃,因为 popup.getContentView().getParent() 不会返回 View,因此投射会导致崩溃。对于这种情况,我使用@BeytanKurt 的方法作为后备。
【解决方案3】:

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

最终在您显示弹出窗口时使其可见,并在您退出弹出窗口时使其可见。

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

【讨论】:

  • 但这不会使操作/工具栏变暗。
  • 请告诉我如何操作工具栏变暗
  • 添加 back_dim_layout.setVisibility(View.GONE);在 PopupWindow.OnDismissListener
【解决方案4】:

另一个技巧是使用 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;
}

【讨论】:

  • 几乎对我有用......有时,弹出窗口位于暗/虚拟弹出窗口后面的竞争条件。还没有找到解决方法...即使延迟弹出窗口的显示以给延迟窗口后面的时间也无济于事:-P
  • @kenyee 我找到了解决此问题的方法。我首先显示“淡入淡出”弹出窗口,然后为了显示第二个弹出窗口,我正在使用 myFadePopup.getContentView().post( ... myPopup.showAtLocation ... );
【解决方案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 上完成

【讨论】:

    【解决方案6】:

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

    而不是使用 popupWindow.getContentView().getParent() 并将结果转换为 View(在 API 级别 16 上崩溃,因为它返回一个不是 View 实例的 ViewRootImpl 对象)我只使用 .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. 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);
        }
    }
    

    【讨论】:

      【解决方案7】:

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

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

      在使用库的情况下,如果需要虚化背景,直接调用setBlurBackgroundEnable(true).

      查看wiki了解更多详情。(zh-cn语言)

      BasePopup:wiki

      【讨论】:

        【解决方案8】:
        findViewById(R.id.drawer_layout).setAlpha((float) 0.7);
        

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

        【讨论】:

          【解决方案9】:

          您可以使用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?
          【解决方案10】:

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

          所以我尝试了这种方式,

          为调光效果创建一个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>
          

          并在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>
          

          最后我在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也可以调暗。

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

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

          【讨论】:

            【解决方案11】:

            此代码有效

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-26
              • 2017-09-19
              相关资源
              最近更新 更多