Размытие или тусклый фон при активном Android PopupWindow


85

Я хотел бы иметь возможность размывать или затемнять фон, когда я показываю свое всплывающее окно, используя popup.showAtLocation, и не размывать / затемнять фон при popup.dismissвызове.

Я попытался применить параметры макета FLAG_BLUR_BEHINDи FLAG_DIM_BEHINDк своей деятельности, но это, похоже, просто размывает и затемняет фон, как только мое приложение запускается.

Как сделать размытие / затемнение только во всплывающих окнах?


1
Предупреждение: FLAG_BLUR_BEHIND не работает на некоторых телефонах и делает телефон очень невосприимчивым (видимо, размытие выполняется программно). Например, на Дроиде. Помимо этого, то, что сказал Макарс, - FLAG_BLUR_BEHIND необходимо применить к окну на переднем плане.
— EboMike


1
Я ищу такой же ответ на этот вопрос. Есть ли способ программно затемнить представление, которое находится за всплывающим окном?
— Ник

1
@Nick Как ты решил эту проблему ..
— Амит,

Ответы:


108

Вопрос был о Popupwindowклассе, но все дали ответы, которые используют этот Dialogкласс. Это бесполезно, если вам нужно использовать Popupwindowкласс, потому что у Popupwindowнего нет getWindow()метода.

Я нашел решение, которое действительно работает Popupwindow. Требуется только, чтобы корень XML-файла, который вы используете для фоновой активности, был FrameLayout. Вы можете присвоить Framelayoutэлементу android:foregroundтег. Этот тег указывает на доступный для рисования ресурс, который будет размещен поверх всего действия (то есть, если Framelayout является корневым элементом в файле xml). Затем вы можете управлять непрозрачностью ( setAlpha()) переднего плана, доступного для рисования.

Вы можете использовать любой ресурс с возможностью рисования, который вам нравится, но если вам просто нужен эффект затемнения, создайте файл 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>

(См. Http://developer.android.com/guide/topics/resources/drawable-resource.html#Shape для получения дополнительной информации об shapeэлементе). Обратите внимание, что я не указал альфа-значение в теге цвета, которое сделало бы доступный для рисования элемент прозрачным (например #ff000000). Причина этого в том, что любое жестко запрограммированное альфа-значение, кажется, переопределяет любые новые альфа-значения, которые мы устанавливаем с помощью setAlpha()в нашем коде, поэтому мы этого не хотим. Однако это означает, что вытягиваемый элемент изначально будет непрозрачным (сплошным, непрозрачным). Поэтому нам нужно сделать его прозрачным в onCreate()методе действия.

Вот код элемента xml Framelayout:

<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

Значения альфа изменяются от 0(непрозрачный) до 255(невидимый). Вы должны убрать яркость активности, когда закрываете Popupwindow.

Я не включил код для отображения и закрытия всплывающего окна, но вот ссылка на то, как это можно сделать: http://www.mobilemancer.com/2011/01/08/popup-window-in-android/


чтобы убедиться, что это работает на старых устройствах, вы также должны аннулировать передний план после каждого изменения, внесенного в его альфа-канал. Я добавил это к ответу. +1 за хороший ответ
— user2224350

1
Я не могу придумать причину, по которой это не помечено как правильный ответ ... отличный ответ, мягко говоря.
— Jayshil Dave

2
Что насчет того, чтобы иметь панель действий, вы не можете ее затемнить
— BaDo

это сработало лучше всего для меня ... ответ с двумя всплывающими окнами ниже имел состояние гонки, поэтому затемненное всплывающее окно иногда было поверх реального всплывающего окна
— kenyee

1
Практически идеально. Единственный недостаток - вам нужно использовать FrameLayout, поскольку для других макетов нет метода getForeground.
— LiangWang 05

81

Так как PopupWindowтолько добавляет Viewк WindowManagerвам можно использовать updateViewLayout (View view, ViewGroup.LayoutParams params)для обновления LayoutParamsваших PopupWindow«с contentViewпосле вызова шоу .. ().

Установка флажка окна 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);

Обновление Marshmallow:

В M PopupWindow оборачивает contentView внутри FrameLayout с именем mDecorView. Если вы покопаетесь в источнике 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 еще не тестировал. Обновим как можно скорее.
— Маркус Рубей

@ 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); } }); }
— Бейтан Курт

1
К вашему сведению, когда я использую это на более низких уровнях API (например, 15 и 20), он вылетает, потому что popup.getContentView (). GetParent () не возвращает View, поэтому его приведение приведет к сбою. В этом случае я использовал метод @ BeytanKurt как запасной вариант.
— Мэтью Хорст,

30

В ваш 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>

В вашей деятельности oncreate

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

1
Пожалуйста, не оставляйте подписи типа «Спасибо, Рупеш» в постах.
— Саймон Хеллингер

3
но это не приведет к затемнению панели действий / инструментов.
— Silvia H

Пожалуйста, скажите мне, как сделать тусклую панель инструментов
— SAndroidD

1
добавить 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;
}

Почти работает для меня ... иногда возникает состояние гонки, когда всплывающее окно находится за тусклым / фиктивным всплывающим окном. Пока не нашел обходного пути ... даже задержка показа всплывающего окна, чтобы дать ему время отстать от отложенного, не помогает :-P
— kenyee

2
@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. Это также можно сделать в родных всплывающих окнах


1

Для меня работает что-то вроде ответа Абдельхака Муаму, протестированного на уровне 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 это идентификатор макета, яркость которого вы хотите уменьшить.


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>

И добавить backgroundв FrameLayoutкачестве корневого тега 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также можно затемнить.

Ps я не говорю, что uhmdown неправильно. Я узнал из его ответа и пытаюсь развить свою проблему. Я тоже запуталась, хороший это способ или нет.

Любые предложения также приветствуются, также извините за мой плохой английский.


0

Может быть, вам поможет это репо: BasePopup

Это мое репо, которое используется для решения различных проблем PopupWindow.

В случае использования библиотеки, если вам нужно размыть фон, просто вызовите setBlurBackgroundEnable(true).

См. Вики для получения более подробной информации. (Язык в zh-cn)

BasePopup: вики


-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);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.