Размытие или затемнение фона при активном всплывающем меню 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);
}
}
Ответ 9
Может быть, это поможет вам.
https://github.com/razerdp/BasePopup
Ответ 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);