Verwischen oder verdunkeln Sie den Hintergrund, wenn Android PopupWindow aktiv ist


85

Ich möchte in der Lage sein, den Hintergrund entweder zu verwischen oder zu popup.showAtLocationdimmen, wenn ich mein Popup-Fenster mit zeige, und den Hintergrund zu verwischen , wenn er popup.dismissaufgerufen wird.

Ich habe versucht, Layoutparameter FLAG_BLUR_BEHINDund FLAG_DIM_BEHINDmeine Aktivität anzuwenden , aber dies scheint den Hintergrund nur zu verwischen und zu dimmen , sobald meine App gestartet wird.

Wie kann ich nur mit Popups verwischen / dimmen?


1
Wort der Warnung: FLAG_BLUR_BEHIND ist auf einigen Telefonen fehlerhaft und macht das Telefon sehr unempfindlich (anscheinend erfolgt die Unschärfe in der Software). Zum Beispiel auf dem Droiden. Abgesehen davon muss das, was Macarse gesagt hat - FLAG_BLUR_BEHIND auf das Fenster im Vordergrund angewendet werden.
EboMike


1
Ich suche die gleiche Antwort auf diese Frage. Gibt es eine Möglichkeit, die Ansicht hinter dem Popup-Fenster programmgesteuert zu dimmen?
Nick

1
@ Nick Wie Sie dieses Problem gelöst haben ..
Amit

Antworten:


106

Die Frage betraf die PopupwindowKlasse, doch jeder hat Antworten gegeben, die die DialogKlasse verwenden. Das ist ziemlich nutzlos, wenn Sie die PopupwindowKlasse verwenden müssen, weil Popupwindowes keine getWindow()Methode gibt.

Ich habe eine Lösung gefunden, die tatsächlich funktioniert Popupwindow. Es ist nur erforderlich, dass das Stammverzeichnis der XML-Datei, die Sie für die Hintergrundaktivität verwenden, a ist FrameLayout. Sie können dem FramelayoutElement ein android:foregroundTag geben. Mit diesem Tag wird eine zeichnbare Ressource angegeben, die über die gesamte Aktivität gelegt wird (dh wenn das Framelayout das Stammelement in der XML-Datei ist). Sie können dann die Deckkraft ( setAlpha()) des Vordergrundzeichens steuern.

Sie können jede beliebige Zeichenressource verwenden, aber wenn Sie nur einen Dimmeffekt wünschen, erstellen Sie eine XML-Datei im Zeichenordner mit dem <shape>Tag als 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>

( Weitere Informationen zum Element finden Sie unter http://developer.android.com/guide/topics/resources/drawable-resource.html#Shapeshape .) Beachten Sie, dass ich im Farbetikett keinen Alpha-Wert angegeben habe, der das zeichnbare Element transparent machen würde (z #ff000000. B. ). Der Grund dafür ist, dass jeder fest codierte Alpha-Wert alle neuen Alpha-Werte zu überschreiben scheint, die wir über den setAlpha()in unserem Code festgelegten Wert festgelegt haben. Das wollen wir also nicht. Dies bedeutet jedoch, dass das zeichnbare Objekt zunächst undurchsichtig ist (fest, nicht transparent). Wir müssen es also in der onCreate()Methode der Aktivität transparent machen .

Hier ist der Framelayout-XML-Elementcode:

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

Hier ist die onCreate () -Methode der Aktivität:

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

Schließlich der Code zum Dimmen der Aktivität:

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

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

Die Alpha-Werte gehen von 0(undurchsichtig) bis 255(unsichtbar). Sie sollten die Aktivität aufheben, wenn Sie das Popup-Fenster schließen.

Ich habe keinen Code zum Anzeigen und Schließen des Popup-Fensters eingefügt, aber hier ist ein Link, wie dies durchgeführt werden kann: http://www.mobilemancer.com/2011/01/08/popup-window-in-android/


Um sicherzustellen, dass dies auch auf alten Geräten funktioniert, müssen Sie den Vordergrund nach jeder Änderung am Alphakanal ungültig machen. Ich habe dies der Antwort hinzugefügt. +1 für die nette Antwort
user2224350

1
Ich kann mir keinen Grund vorstellen, warum dies nicht als die richtige Antwort markiert ist. Eine großartige Antwort, um es gelinde auszudrücken.
Jayshil Dave

2
Was ist mit in Sicht haben Actionbar, Sie können es nicht
dimmen

das hat am besten für mich funktioniert ... die doppelte Popup-Antwort unten hatte eine Rennbedingung, so dass das abgeblendete Popup manchmal über dem echten Popup lag
Kenyee

1
Fast perfekt. Der einzige Nachteil ist, dass Sie FrameLayout verwenden müssen, da es für andere Layouts keine "getForeground" -Methode gibt.
LiangWang

79

Da PopupWindownur ein fügt Viewzu WindowManagerkönnen Sie mit updateViewLayout (View view, ViewGroup.LayoutParams params)der Aktualisierung LayoutParamsIhrer PopupWindow‚s contentViewAfter - Show - Aufruf .. ().

Durch Setzen der Fensterflagge FLAG_DIM_BEHINDwird alles hinter dem Fenster abgeblendet. Verwenden Sie dimAmountdiese Option , um die Menge an Dim zu steuern (1,0 für vollständig undurchsichtig bis 0,0 für No Dim).

Denken Sie daran, dass wenn Sie einen Hintergrund für Ihren festlegen, PopupWindowdieser contentViewin einen Container verschoben wird, was bedeutet, dass Sie dessen übergeordnetes Element aktualisieren müssen.

Mit Hintergrund:

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

Ohne Hintergrund:

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 Update:

In M PopupWindow wird die Inhaltsansicht in ein FrameLayout mit dem Namen mDecorView eingeschlossen. Wenn Sie sich in die PopupWindow-Quelle vertiefen, finden Sie so etwas wie. createDecorView(View contentView)Der Hauptzweck von mDecorView besteht darin, Ereignisversand und Inhaltsübergänge zu verarbeiten, die für M neu sind. Dies bedeutet, dass wir eine weitere .getParent () hinzufügen müssen, um auf den Container zuzugreifen.

Mit Hintergrund, der eine Änderung an etwas erfordern würde wie:

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

Bessere Alternative für API 18+

Eine weniger hackige Lösung mit ViewGroupOverlay:

1) Holen Sie sich das gewünschte Root-Layout

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

2) Rufen Sie an applyDim(root, 0.5f);oderclearDim()

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
Dies scheint auf Geräten mit Android 6.0 Marshmallow nicht mehr zu funktionieren. Die layoutParams werden nicht umgewandelt WindowManager.LayoutParams. Gibt es eine Problemumgehung?
Robert

Vielen Dank für den Hinweis. Ich habe es noch nicht auf M getestet. Wird so schnell wie möglich aktualisiert.
Markus Rubey

@ stackoverflow.com/users/308153/robert Ich hatte auch das gleiche Problem. Ich habe den obigen Code mit Hintergrund verwendet.
Rizwan Sohaib

Wenn p null ist, verwenden Sie dies: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
Zu Ihrer Information Wenn ich dies auf niedrigeren API-Ebenen (z. B. 15 und 20) verwende, stürzt es ab, weil popup.getContentView (). GetParent () View nicht zurückgibt, sodass das Casting einen Absturz verursacht. In diesem Fall habe ich die Methode von @ BeytanKurt als Fallback verwendet.
Matthew Horst

29

Fügen Sie in Ihrer XML-Datei so etwas mit Breite und Höhe als 'match_parent' hinzu.

<RelativeLayout
        android:id="@+id/bac_dim_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#C0000000"
        android:visibility="gone" >
</RelativeLayout>

In Ihrer Aktivität oncreate

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

Machen Sie es schließlich sichtbar, wenn Sie Ihr Popup-Fenster anzeigen, und machen Sie es sichtbar, wenn Sie das Popup-Fenster verlassen.

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

1
Bitte hinterlassen Sie keine Unterschriften wie "Danke, Rupesh" in Posts.
Simon Hellinger

3
Dadurch wird die Aktions- / Symbolleiste jedoch nicht verkleinert.
Silvia H

Bitte sagen Sie mir, wie man die Symbolleiste
dimmt

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

sicher hat mir geholfen
Pixxu Waku

12

Ein weiterer Trick besteht darin, 2 Popup-Fenster anstelle von einem zu verwenden. Das erste Popup-Fenster ist einfach eine Dummy-Ansicht mit durchscheinendem Hintergrund, die den Dimm-Effekt liefert. Das 2. Popup-Fenster ist Ihr beabsichtigtes Popup-Fenster.

Reihenfolge beim Erstellen von Popup-Fenstern: Zeigen Sie zuerst das Dummy-Popup-Fenster und dann das gewünschte Popup-Fenster an.

Reihenfolge beim Zerstören: Schließen Sie das beabsichtigte Popup-Fenster und dann das Dummy-Popup-Fenster.

Die beste Möglichkeit, diese beiden zu verknüpfen, besteht darin, einen OnDismissListener hinzuzufügen und die onDismiss()Methode zu überschreiben, mit der das Dummy-Popup-Fenster von ihrem Fenster entfernt werden soll.

Code für das Dummy-Popup-Fenster:

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>

Popup anzeigen, um den Hintergrund zu dimmen

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

Funktioniert fast für mich ... manchmal gibt es eine Rennbedingung, bei der sich das Popup-Fenster hinter dem Dim / Dummy-Popup-Fenster befindet. Ich habe noch keine Problemumgehung gefunden ... selbst wenn das Anzeigen des Popup-Fensters verzögert wird, um Zeit hinter dem verzögerten Fenster zu haben, hilft dies nicht :-P
Kenyee

2
@kenyee Ich habe eine Lösung für dieses Problem gefunden. Ich zeige zuerst das Popup "Fade" und dann, um das zweite Popup anzuzeigen, verwende ich myFadePopup.getContentView (). Post (... myPopup.showAtLocation ...);
Piotr

5

Ich habe eine Lösung dafür gefunden

Erstellen Sie einen benutzerdefinierten transparenten Dialog und öffnen Sie in diesem Dialog das Popup-Fenster:

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 für den Dialog (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" />

Jetzt möchten Sie den Dialog schließen, wenn das Popup-Fenster geschlossen wird. so

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

Hinweis: Ich habe hier das NewQuickActionPlugin zum Erstellen von PopupWindow verwendet. Dies kann auch unter nativen Popup-Fenstern erfolgen


1

Für mich funktioniert so etwas wie die Antwort von Abdelhak Mouaamou, getestet auf API-Level 16 und 27.

Anstatt popupWindow.getContentView().getParent()das Ergebnis zu verwenden und umzuwandeln View(was auf API-Ebene 16 abstürzt, weil dort ein ViewRootImplObjekt zurückgegeben wird, von dem es keine Instanz ist View), verwende ich nur, .getRootView()das bereits eine Ansicht zurückgibt, sodass dort kein Casting erforderlich ist.

Hoffe es hilft jemandem :)

Vollständiges Arbeitsbeispiel, das aus anderen Stackoverflow-Posts zusammengemischt wurde. Kopieren Sie es einfach und fügen Sie es ein, z. B. im onClick-Listener einer Schaltfläche:

// 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 ist die ID des Layouts, dessen Helligkeit Sie verringern möchten.


0

Sie können das verwenden android:theme="@android:style/Theme.Dialog", um das zu tun. Erstellen Sie eine Aktivität und AndroidManifest.xmldefinieren Sie die Aktivität in:

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

Dies scheint nicht den Trick zu tun. Ich habe dies auf meine einzige Aktivität angewendet, die sich im Hintergrund befindet, wenn ich ein Popup-Fenster zeige, das nur ein aufgeblähtes Layout ist und keine separate Aktivität. Wie wende ich FLAG_BLUR_BEHIND auf ein PopupWindow an?
k_day

0

ok, also folge ich der Antwort von uhmdown zum Dimmen der Hintergrundaktivität, wenn das Pop-Fenster geöffnet ist. Aber es schafft Probleme für mich. Es handelte sich um eine Dimmaktivität und ein Popup-Fenster (bedeutet, dass sowohl die Aktivität als auch das Popup schwarz gedimmt sind und nicht voneinander getrennt werden können).

also habe ich es so versucht,

Erstellen Sie eine Datei dimming_black.xml für den Dimmeffekt .

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

Und füge wie backgroundin FrameLayoutals Root-XML-Tag hinzu, füge auch meine anderen Steuerelemente LinearLayoutso einlayout.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>

Endlich zeige ich Popup auf meinem MainActivitymit einem zusätzlichen Parametersatz wie unten.

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

Ergebnis: Geben Sie hier die Bildbeschreibung ein

es funktioniert bei mir auch gelöstes problem wie von baDo kommentiert . Damit Actionbarkann auch gedimmt werden.

Ps ich sage nicht uhmdown's ist falsch. Ich habe aus seiner Antwort gelernt und versucht, mich für mein Problem weiterzuentwickeln. Ich habe auch verwirrt, ob dies ein guter Weg ist oder nicht.

Irgendwelche Vorschläge werden auch geschätzt, auch entschuldigen Sie mein schlechtes Englisch.


0

Vielleicht hilft Ihnen dieses Repo: BasePopup

Dies ist mein Repo, mit dem verschiedene Probleme von PopupWindow gelöst werden.

Wenn Sie bei Verwendung der Bibliothek den Hintergrund verwischen müssen, rufen Sie einfach an setBlurBackgroundEnable(true).

Weitere Informationen finden Sie im Wiki. (Sprache in zh-cn)

BasePopup: Wiki


-1

Dieser Code funktioniert

        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);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.