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

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

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

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

Ответ 1

Вопрос был о классе 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 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.

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

Ответ 2

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

Установка флага окна 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 - обрабатывать пересылки событий и переходы контента, которые являются новыми для М. Это означает, что нам нужно добавить еще один .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();
}

Ответ 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>

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

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

Ответ 4

Другим трюком является использование 2 всплывающих окон вместо одного. Первое всплывающее окно будет просто фиктивным представлением с полупрозрачным фоном, который обеспечивает эффект тусклого эффекта. Второе всплывающее окно - это ваше всплывающее окно.

Последовательность при создании всплывающих окон: Покажите фиктивное всплывающее окно 1-го и затем всплывающее окно.

Последовательность при уничтожении: Отключите всплывающее окно, а затем всплывающее окно.

Лучший способ связать эти два состоит в том, чтобы добавить 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;
}

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

Ответ 6

Вы можете использовать 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">

Ответ 7

хорошо, так что я следую за ответом 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 с некоторым дополнительным набором параметров, как 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);

Результат: enter image description here

это работает для меня, также решена проблема, как прокомментировал BaDo. С помощью этой Actionbar также можно Actionbar.

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

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

Ответ 8

Для меня что-то вроде ответа Abdelhak Mouaamou работает, протестировано на уровне API 16 и 27.

Вместо использования popupWindow.getContentView().getParent() и popupWindow.getContentView().getParent() результата в View (что приводит к ViewRootImpl на уровне 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. 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);
    }
}

Ответ 10

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

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

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

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

BasePopup: вики

Ответ 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);