Sfoca o oscura lo sfondo quando Android PopupWindow è attivo


85

Vorrei essere in grado di sfocare o attenuare lo sfondo quando mostro la mia finestra popup utilizzando popup.showAtLocatione sbloccare / attenuare lo sfondo quando popup.dismissviene chiamato.

Ho provato ad applicare i parametri di layout FLAG_BLUR_BEHINDe FLAG_DIM_BEHINDalla mia attività, ma questo sembra sfocare e oscurare lo sfondo non appena la mia app viene avviata.

Come posso sfocare / attenuare solo con i popup?


1
Un avvertimento: FLAG_BLUR_BEHIND presenta bug su alcuni telefoni e rende il telefono molto insensibile (a quanto pare la sfocatura viene eseguita nel software). Sul Droid, per esempio. Oltre a questo, quello che ha detto Macarse - FLAG_BLUR_BEHIND deve essere applicato alla finestra in primo piano.
EboMike


1
Sto cercando la stessa risposta a questa domanda. C'è un modo per oscurare programmaticamente la vista che si trova dietro la finestra popup?
Nick

1
@ Nick Come hai risolto questo problema ..
Amit

Risposte:


108

La domanda riguardava la Popupwindowclasse, eppure tutti hanno dato risposte che usano la Dialogclasse. È praticamente inutile se hai bisogno di usare la Popupwindowclasse, perché Popupwindownon ha un getWindow()metodo.

Ho trovato una soluzione che funziona davvero con Popupwindow. Richiede solo che la radice del file xml che utilizzi per l'attività in background sia un file FrameLayout. Puoi assegnare Framelayoutun android:foregroundtag all'elemento . Ciò che fa questo tag è specificare una risorsa disegnabile che verrà sovrapposta all'intera attività (cioè, se Framelayout è l'elemento radice nel file xml). È quindi possibile controllare l'opacità ( setAlpha()) del disegnabile in primo piano.

Puoi usare qualsiasi risorsa disegnabile che ti piace, ma se vuoi solo un effetto di attenuazione, crea un file xml nella cartella disegnabile con il <shape>tag come 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>

(Vedi http://developer.android.com/guide/topics/resources/drawable-resource.html#Shape per maggiori informazioni shapesull'elemento). Nota che non ho specificato un valore alfa nel tag color che renderebbe trasparente l'elemento disegnabile (ad esempio #ff000000). La ragione di ciò è che qualsiasi valore alpha hardcoded sembra sovrascrivere qualsiasi nuovo valore alfa che abbiamo impostato tramite il setAlpha()nostro codice, quindi non lo vogliamo. Tuttavia, ciò significa che l'elemento disegnabile sarà inizialmente opaco (solido, non trasparente). Quindi dobbiamo renderlo trasparente nel onCreate()metodo dell'attività .

Ecco il codice dell'elemento xml di 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>

Ecco il metodo onCreate () dell'attività:

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

Infine, il codice per attenuare l'attività:

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

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

I valori alfa vanno da 0(opaco) a 255(invisibile). È necessario disattivare l'attività quando si chiude la finestra Popup.

Non ho incluso il codice per mostrare e chiudere la finestra Popup, ma ecco un link a come farlo: http://www.mobilemancer.com/2011/01/08/popup-window-in-android/


per assicurarti che funzioni anche sui vecchi dispositivi devi invalidare il primo piano dopo ogni modifica apportata al suo canale alfa. Ho aggiunto questo alla risposta. +1 per la bella risposta
user2224350

1
Non riesco a pensare a un motivo per cui questo non è contrassegnato come la risposta giusta .. risposta fantastica per non dire altro.
Jayshil Dave

2
Che ne dici di avere Actionbar, non
puoi abbassarlo

questo ha funzionato meglio per me ... la risposta del doppio popup di seguito aveva una condizione di gara, quindi il popup oscurato a volte era sopra il popup reale
Kenyee

1
Quasi perfetto. L'unico svantaggio è che devi usare FrameLayout poiché non esiste un metodo "getForeground" per altri layout.
LiangWang

81

Dal momento che PopupWindowsolo aggiunge un Viewper WindowManagerè possibile utilizzare updateViewLayout (View view, ViewGroup.LayoutParams params)per aggiornare la LayoutParamsdei vostri PopupWindow's contentViewdopo aver chiamato spettacolo .. ().

L'impostazione della bandiera della finestra FLAG_DIM_BEHINDoscurerà tutto ciò che si trova dietro la finestra. Utilizzare dimAmountper controllare la quantità di attenuazione (1.0 per completamente opaco a 0.0 per nessuna attenuazione).

Tieni presente che se imposti uno sfondo al tuo PopupWindow, il tuo verrà inserito contentViewin un contenitore, il che significa che dovrai aggiornare il suo genitore.

Con sfondo:

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

Senza sfondo:

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

Aggiornamento Marshmallow:

Su M PopupWindow avvolge il contentView all'interno di un FrameLayout chiamato mDecorView. Se scavi nella sorgente PopupWindow troverai qualcosa di simile createDecorView(View contentView). Lo scopo principale di mDecorView è quello di gestire l'invio di eventi e le transizioni di contenuto, che sono nuove per M. Ciò significa che dobbiamo aggiungere un altro .getParent () per accedere al contenitore.

Con uno sfondo che richiederebbe una modifica a qualcosa come:

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

Migliore alternativa per API 18+

Una soluzione meno hacker che utilizza ViewGroupOverlay:

1) Procurati il ​​layout radice desiderato

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

2) Chiama applyDim(root, 0.5f);oclearDim()

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
Sembra che non funzioni più sui dispositivi con Android 6.0 Marshmallow. Il layoutParams non trasmette a WindowManager.LayoutParams. C'è una soluzione alternativa?
Robert

Grazie per segnalarlo. Non l'ho ancora testato su M. Aggiornerà al più presto.
Markus Rubey

@ stackoverflow.com/users/308153/robert Ho avuto anche lo stesso problema. Ho usato il codice sopra con lo sfondo.
Rizwan Sohaib

Se p è nullo, usa questo: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 Quando lo uso su livelli API inferiori (ad esempio 15 e 20) si arresta in modo anomalo perché popup.getContentView (). GetParent () non restituisce View, quindi il casting provocherà un arresto anomalo. In questo caso ho utilizzato il metodo di @ BeytanKurt come riserva.
Matthew Horst

30

Nel tuo file xml aggiungi qualcosa di simile con larghezza e altezza come "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>

Nella tua attività crea

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

Infine rendilo visibile quando mostri la tua finestra popup e rendilo visibile quando esci da popupwindow.

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

1
Per favore, non lasciare firme come "Grazie, Rupesh" nei post.
Simon Hellinger

3
ma questo non oscurerà la barra delle azioni / degli strumenti.
Silvia H

Per favore dimmi come ridurre la barra degli strumenti delle azioni
SAndroidD

1
aggiungi back_dim_layout.setVisibility (View.GONE); in PopupWindow.OnDismissListener
Pulkit

sicuramente mi ha aiutato
Pixxu Waku

12

Un altro trucco è usare 2 finestre popup invece di una. La prima finestra popup sarà semplicemente una vista fittizia con sfondo traslucido che fornisce l'effetto fioco. La seconda finestra popup è la finestra popup prevista.

Sequenza durante la creazione delle finestre popup: mostra prima la finestra popup fittizia e quindi la finestra popup desiderata.

Sequenza durante la distruzione: Chiudi la finestra a comparsa prevista e quindi la finestra a comparsa fittizia.

Il modo migliore per collegare questi due è aggiungere un OnDismissListener e sovrascrivere il onDismiss()metodo previsto per dimissare la finestra popup fittizia dal loro file.

Codice per la finestra popup fittizia:

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>

Mostra popup dissolvenza per attenuare lo sfondo

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

Quasi funziona per me ... a volte, c'è una condizione di gara in cui la finestra popup si trova dietro la finestra popup oscura / fittizia. Non ho ancora trovato una soluzione alternativa ... anche ritardare la visualizzazione della
finestra popup

2
@kenyee Ho trovato una soluzione per questo problema. Mostra prima il popup 'fade', quindi per mostrare il secondo popup sto usando myFadePopup.getContentView (). Post (... myPopup.showAtLocation ...);
Piotr

5

Ho trovato una soluzione per questo

Crea una finestra di dialogo trasparente personalizzata e all'interno di quella finestra di dialogo apri la finestra popup:

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 per la finestra di dialogo (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" />

ora si desidera chiudere la finestra di dialogo quando la finestra Popup viene chiusa. così

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

Nota: ho usato il NewQuickActionplugin per creare PopupWindow qui. Può anche essere fatto su Windows Popup nativo


1

Per me, qualcosa come la risposta di Abdelhak Mouaamou funziona, testata a livello API 16 e 27.

Invece di utilizzare popupWindow.getContentView().getParent()e eseguire il cast del risultato View(che si arresta in modo anomalo al livello API 16 perché restituisce un ViewRootImploggetto che non è un'istanza di View), io uso semplicemente .getRootView()che restituisce già una vista, quindi non è richiesto il casting.

Spero che aiuti qualcuno :)

esempio di lavoro completo mescolato insieme da altri post di stackoverflow, basta copiarlo e incollarlo, ad esempio, nel listener onClick di un pulsante:

// 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 è l'id del layout di cui vuoi attenuare la luminosità.


0

Puoi usare android:theme="@android:style/Theme.Dialog"per farlo. Crea un'attività e AndroidManifest.xmldefinisci l'attività come:

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

Questo non sembra fare il trucco. L'ho applicato alla mia unica attività, che è in background quando mostro una finestra popup che è solo un layout gonfiato che ho e non un'attività separata. Come applico FLAG_BLUR_BEHIND a una finestra Popup?
k_day

0

ok, quindi seguo quello di uhmdown risposta per l'oscuramento dell'attività in background quando la finestra pop è aperta. Ma mi crea problemi. stava oscurando l'attività e includendo la finestra popup (significa che il nero oscurato è a strati sia sull'attività che sul popup, non può essere separato).

quindi ho provato in questo modo

creare un file dimming_black.xml per l'effetto dimming,

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

E aggiungere come backgroundin FrameLayoutcome tag principale xml, anche mettere i miei altri controlli in LinearLayoutcome questolayout.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>

finalmente mostro popup sul mio MainActivitycon alcuni parametri extra impostati come di seguito.

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

Risultato: inserisci qui la descrizione dell'immagine

per me funziona, anche problema risolto come commentato da BaDo . Con questo Actionbarpuò anche essere oscurato.

Ps non sto dicendo che uhmdown sia sbagliato. ho imparato dalla sua risposta e cerco di evolvermi per il mio problema. Ho anche confuso se questo è un buon modo o no.

Qualsiasi suggerimento è apprezzato anche scusate anche per il mio cattivo inglese.


0

Forse questo repo ti aiuterà: BasePopup

Questo è il mio repository, che viene utilizzato per risolvere vari problemi di PopupWindow.

Nel caso di utilizzo della libreria, se è necessario sfocare lo sfondo, è sufficiente chiamare setBlurBackgroundEnable(true).

Vedi il wiki per maggiori dettagli. (Lingua in zh-cn)

BasePopup: wiki


-1

Questo codice funziona

        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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.