Rozmywaj lub przyciemniaj tło, gdy aktywny jest Android PopupWindow


85

Chciałbym mieć możliwość rozmywania lub przyciemniania tła, gdy wyświetlam wyskakujące okienko za pomocą popup.showAtLocation, i rozmywania / przyciemniania tła po popup.dismisswywołaniu.

Próbowałem zastosować parametry układu FLAG_BLUR_BEHINDi FLAG_DIM_BEHINDdo mojej aktywności, ale wydaje się, że tło po prostu rozmywa się i ściemnia, gdy tylko moja aplikacja jest uruchomiona.

Jak mogę rozmywać / ściemniać tylko wyskakujące okienka?


1
Słowo ostrzeżenia: FLAG_BLUR_BEHIND zawiera błędy na niektórych telefonach i sprawia, że ​​telefon bardzo nie reaguje (najwyraźniej rozmycie jest wykonywane w oprogramowaniu). Na przykład na Droidzie. Poza tym, co powiedział Macarse - FLAG_BLUR_BEHIND należy zastosować do okna na pierwszym planie.
EboMike,


1
Szukam tej samej odpowiedzi na to pytanie. Czy istnieje sposób, aby programowo przyciemnić widok znajdujący się za oknem wyskakującym?
Nick

1
@Nick Jak rozwiązałeś ten problem ...
Amit

Odpowiedzi:


107

Pytanie dotyczyło Popupwindowklasy, ale każdy udzielił odpowiedzi wykorzystujących tę Dialogklasę. Jest to prawie bezużyteczne, jeśli musisz użyć Popupwindowklasy, ponieważ Popupwindownie ma getWindow()metody.

Znalazłem rozwiązanie, które faktycznie działa Popupwindow. Wymaga tylko, aby katalog główny pliku xml, którego używasz do działania w tle, to plik FrameLayout. Możesz nadać Framelayoutelementowi android:foregroundtag. Ten znacznik określa zasób, który można rysować, który zostanie nałożony na całą aktywność (to znaczy, jeśli Framelayout jest elementem głównym w pliku xml). Następnie możesz kontrolować krycie ( setAlpha()) rysowanego pierwszego planu.

Możesz użyć dowolnego zasobu do rysowania, który Ci się podoba, ale jeśli chcesz uzyskać efekt ściemniania, utwórz plik xml w folderze do rysowania z <shape>tagiem jako głównym.

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

( Więcej informacji na temat elementu można znaleźć pod adresem http://developer.android.com/guide/topics/resources/drawable-resource.html#Shapeshape ). Zauważ, że nie podałem wartości alfa w znaczniku koloru, która sprawiłaby, że element do rysowania byłby przezroczysty (np #ff000000.). Powodem tego jest to, że jakakolwiek zakodowana na stałe wartość alfa zdaje się zastępować wszelkie nowe wartości alfa, które ustawiliśmy za pomocą setAlpha()w naszym kodzie, więc nie chcemy tego. Oznacza to jednak, że element do wyciągania będzie początkowo nieprzezroczysty (jednolity, nieprzezroczysty). Musimy więc uczynić go przejrzystym w onCreate()metodzie działania.

Oto kod elementu 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>

Oto metoda onCreate () działania:

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

Na koniec kod do ściemniania aktywności:

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

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

Wartości alfa przechodzą od 0(nieprzezroczysty) do 255(niewidoczny). Powinieneś cofnąć przyciemnienie działania po zamknięciu Popupwindow.

Nie załączyłem kodu do wyświetlania i zamykania Popupwindow, ale oto link do tego, jak to zrobić: http://www.mobilemancer.com/2011/01/08/popup-window-in-android/


aby upewnić się, że działa to również na starych urządzeniach, musisz unieważnić pierwszy plan po każdej zmianie dokonanej w jego kanale alfa. Dodałem to do odpowiedzi. +1 za miłą odpowiedź
user2224350

1
Nie mogę wymyślić powodu, dla którego nie jest to zaznaczone jako właściwa odpowiedź… niesamowita odpowiedź delikatnie mówiąc.
Jayshil Dave

2
A co z widokiem na Actionbar, nie możesz go przyciemnić
BaDo

to działało najlepiej dla mnie ... podwójna odpowiedź wyskakująca poniżej miała stan wyścigu, więc przyciemnione wyskakujące okienko czasami znajdowało się nad prawdziwym wyskakującym
okienkiem

1
Prawie idealnie. Jedyną wadą jest to, że musisz używać FrameLayout, ponieważ nie ma metody „getForeground” dla innych układów.
LiangWang

80

Ponieważ PopupWindowpo prostu dodaje Viewsię do WindowManagerwas może wykorzystać updateViewLayout (View view, ViewGroup.LayoutParams params)do zaktualizowania LayoutParamsswoich PopupWindow„s contentViewpo wywołaniu pokaz .. ().

Ustawienie flagi okna FLAG_DIM_BEHINDspowoduje przyciemnienie wszystkiego za oknem. Służy dimAmountdo kontrolowania ilości przyciemnienia (od 1,0 dla całkowitego nieprzezroczystości do 0,0 dla braku przyciemnienia).

Pamiętaj, że jeśli ustawisz tło na swoje PopupWindow, umieści contentViewje w kontenerze, co oznacza, że ​​musisz zaktualizować jego rodzica.

Z tłem:

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

Bez tła:

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

Aktualizacja Marshmallow:

W M PopupWindow otacza contentView wewnątrz FrameLayout o nazwie mDecorView. Jeśli zagłębisz się w źródło PopupWindow, znajdziesz coś w rodzaju createDecorView(View contentView).Głównym celem mDecorView jest obsługa wysyłania zdarzeń i przejść treści, które są nowe dla M. Oznacza to, że musimy dodać jeszcze jedną .getParent (), aby uzyskać dostęp do kontenera.

Z tłem, które wymagałoby zmiany na coś takiego:

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

Lepsza alternatywa dla API 18+

Mniej hakerskie rozwiązanie wykorzystujące ViewGroupOverlay:

1) Zdobądź pożądany układ katalogu głównego

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

2) Zadzwoń applyDim(root, 0.5f);lubclearDim()

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
Wydaje się, że to już nie działa na urządzeniach z Androidem 6.0 Marshmallow. LayoutParams nie rzutuje do WindowManager.LayoutParams. Czy jest w pobliżu praca?
Robert

Dziękuję za zwrócenie uwagi. Nie testowałem jeszcze tego na M. Zaktualizuje jak najszybciej.
Markus Rubey

@ stackoverflow.com/users/308153/robert Miałem też ten sam problem. Użyłem powyższego kodu z tłem.
Rizwan Sohaib

Jeśli p jest zerowe, użyj tego: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
FYI Kiedy używam tego na niższych poziomach API (np. 15 i 20), wywala się, ponieważ popup.getContentView (). GetParent () nie zwraca View, więc rzutowanie spowoduje awarię. W tym przypadku jako rezerwę użyłem metody @ BeytanKurt.
Matthew Horst,

29

W pliku xml dodaj coś takiego z szerokością i wysokością jako „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>

W swojej działalności oncreate

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

Na koniec spraw, aby było widoczne, kiedy pokazujesz swoje okno podręczne i znikało, gdy wychodzisz z tego okna.

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

1
Prosimy nie zostawiać w postach podpisów typu „Dzięki, Rupesh”.
Simon Hellinger

3
ale to nie przyciemni paska akcji / narzędzi.
Silvia H

Plz powiedz mi, jak przyciemnić pasek narzędzi działania
SAndroidD

dodaj back_dim_layout.setVisibility (View.GONE); w PopupWindow.OnDismissListener
Pulkit

z pewnością pomogło mi
Pixxu Waku

12

Inną sztuczką jest użycie 2 wyskakujących okienek zamiast jednego. Pierwsze wyskakujące okienko będzie po prostu fałszywym widokiem z przezroczystym tłem, które zapewnia efekt przyciemnienia. Drugie wyskakujące okienko jest Twoim zamierzonym wyskakującym okienkiem.

Sekwencja podczas tworzenia wyskakujących okienek: Pokaż najpierw fałszywe wyskakujące okienko, a następnie żądane wyskakujące okienko.

Sekwencja podczas niszczenia: Zamknij żądane wyskakujące okienko, a następnie fałszywe okno wyskakujące.

Najlepszym sposobem na połączenie tych dwóch jest dodanie OnDismissListener i zastąpienie onDismiss()metody zamierzonej do przyciemnienia fikcyjnego okna wyskakującego z ich.

Kod fikcyjnego okna podręcznego:

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>

Pokaż wyskakujące okienko, aby przyciemnić tło

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

Prawie działa dla mnie ... czasami występuje sytuacja wyścigu, w której wyskakujące okno znajduje się za przyciemnionym / obojętnym oknem wyskakującym. Nie znalazłem jeszcze obejścia ... nawet opóźnienie wyświetlania wyskakującego okienka, aby dać mu czas za opóźnionym, nie pomaga :-P
kenyee

2
@kenyee Znalazłem rozwiązanie tego problemu. Najpierw pokazuję wyskakujące okienko, a następnie, aby pokazać drugie okienko, używam myFadePopup.getContentView (). Post (... myPopup.showAtLocation ...);
Piotr

5

Znalazłem na to rozwiązanie

Utwórz niestandardowe przezroczyste okno dialogowe i w nim otwórz okno podręczne:

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 dla okna dialogowego (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" />

teraz chcesz zamknąć okno dialogowe po zamknięciu okna Popup. więc

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

Uwaga: użyłem NewQuickActionwtyczki do tworzenia PopupWindow tutaj. Można to również zrobić w natywnych okienkach Popup


1

Dla mnie działa coś w rodzaju odpowiedzi Abdelhaka Mouaamou, przetestowane na poziomie API 16 i 27.

Zamiast używać popupWindow.getContentView().getParent()i rzutować wynik do View(co ulega awarii na poziomie API 16, ponieważ zwraca ViewRootImplobiekt, który nie jest instancją View), po prostu używam, .getRootView()który zwraca już widok, więc nie jest wymagane rzutowanie.

Mam nadzieję, że to komuś pomoże :)

kompletny przykład roboczy zaszyfrowany razem z innych postów stackoverflow, po prostu skopiuj go i wklej, np. w odbiorniku onClick przycisku:

// 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 to identyfikator układu, którego jasność chcesz przyciemnić.


0

Możesz użyć android:theme="@android:style/Theme.Dialog"do tego. Utwórz działanie i AndroidManifest.xmlzdefiniuj je jako:

    <activity android:name=".activities.YourActivity"
              android:label="@string/your_activity_label"
              android:theme="@android:style/Theme.Dialog">

Wydaje się, że to nie pomaga. Zastosowałem to do mojej jedynej czynności, która jest w tle, kiedy pokazuję wyskakujące okienko, które jest po prostu zawyżonym układem, który mam, a nie osobnym działaniem. Jak zastosować FLAG_BLUR_BEHIND w PopupWindow?
k_day

0

ok, więc podążam za odpowiedzią uhmdown dotyczącą ściemniania aktywności w tle, gdy okno pop jest otwarte. Ale stwarza to dla mnie problem. była to aktywność ściemniania i dołączanie wyskakującego okienka (oznacza to, że przyciemniona czerń jest nałożona zarówno na aktywność, jak i wyskakujące okienko, nie można ich rozdzielić).

więc próbowałem w ten sposób

utwórz plik dimming_black.xml dla efektu ściemniania,

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

I dodać backgroundw FrameLayoutjak tagu bazowy XML, również umieścić moje inne kontrole w LinearLayouttakilayout.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>

w końcu pokazuję wyskakujące okienko na moim MainActivityz dodatkowym zestawem parametrów, jak poniżej.

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

Wynik: wprowadź opis obrazu tutaj

to działa dla mnie, również rozwiązany problem jak skomentował BaDo . Dzięki temu Actionbarrównież można przyciemnić.

Ps, nie mówię, że uhmdown jest źle. nauczyłem się z jego odpowiedzi i staram się rozwijać dla mojego problemu. Nie wiedziałem też, czy to dobry sposób, czy nie.

Wszelkie sugestie są również mile widziane i przepraszam za mój zły angielski.


0

Może to repozytorium pomoże ci: BasePopup

To jest moje repozytorium, które służy do rozwiązywania różnych problemów PopupWindow.

W przypadku korzystania z biblioteki, jeśli potrzebujesz rozmyć tło, wystarczy zadzwonić setBlurBackgroundEnable(true).

Zobacz wiki po więcej szczegółów. (Język w zh-cn)

BasePopup: wiki


-1

Ten kod działa

        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);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.