Desfocar ou escurecer o fundo quando o Android PopupWindow estiver ativo


85

Eu gostaria de poder desfocar ou escurecer o fundo quando eu mostrar minha janela pop-up usando popup.showAtLocation, e desfocar / escurecer o fundo quando popup.dismissfor chamado.

Tentei aplicar parâmetros de layout FLAG_BLUR_BEHINDe FLAG_DIM_BEHINDà minha atividade, mas isso parece apenas embaçar e escurecer o fundo assim que meu aplicativo é iniciado.

Como posso desfocar / escurecer apenas com pop-ups?


1
Aviso: FLAG_BLUR_BEHIND apresenta erros em alguns telefones e faz com que o telefone não responda (aparentemente o desfoque é feito no software). No Droid, por exemplo. Fora isso, o que Macarse disse - FLAG_BLUR_BEHIND precisa ser aplicado à janela em primeiro plano.
— EboMike


1
Estou procurando a mesma resposta para esta pergunta. Existe alguma maneira de escurecer programaticamente a visualização que está atrás da janela pop-up?
— Nick

1
@Nick Como você resolveu esse problema ..
— Amit

Respostas:


108

A pergunta era sobre a Popupwindowaula, mas todos deram respostas que usam a Dialogaula. Isso é praticamente inútil se você precisar usar a Popupwindowclasse, porque Popupwindownão tem um getWindow()método.

Eu encontrei uma solução que realmente funciona Popupwindow. Requer apenas que a raiz do arquivo xml que você usa para a atividade em segundo plano seja a FrameLayout. Você pode dar ao Framelayoutelemento uma android:foregroundtag. O que essa tag faz é especificar um recurso drawable que será colocado em camadas sobre toda a atividade (ou seja, se o Framelayout for o elemento raiz no arquivo xml). Você pode então controlar a opacidade ( setAlpha()) do drawable do primeiro plano.

Você pode usar qualquer recurso drawable que desejar, mas se quiser apenas um efeito de escurecimento, crie um arquivo xml na pasta drawable com a <shape>tag como raiz.

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

(Consulte http://developer.android.com/guide/topics/resources/drawable-resource.html#Shape para obter mais informações sobre o shapeelemento). Observe que não especifiquei um valor alfa na tag de cor que tornaria o item drawable transparente (por exemplo #ff000000). A razão para isso é que qualquer valor alfa codificado parece substituir quaisquer novos valores alfa que definimos por meio do setAlpha()em nosso código, então não queremos isso. No entanto, isso significa que o item drawable inicialmente será opaco (sólido, não transparente). Portanto, precisamos torná-lo transparente no onCreate()método da atividade .

Este é o código do elemento 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's content
...
</FrameLayout>

Este é o método onCreate () da Activity:

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

Finalmente, o código para escurecer a atividade:

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

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

Os valores alfa vão de 0(opaco) a 255(invisível). Você deve apagar a atividade ao fechar a janela Popup.

Não incluí o código para mostrar e dispensar o Popupwindow, mas aqui está um link de como isso pode ser feito: http://www.mobilemancer.com/2011/01/08/popup-window-in-android/


para garantir que isso funcione em dispositivos antigos, você também deve invalidar o primeiro plano após cada alteração feita em seu canal alfa. Eu adicionei isso à resposta. +1 pela boa resposta
— user2224350

1
Não consigo pensar em uma razão pela qual isso não seja marcado como a resposta certa .. resposta incrível para dizer o mínimo.
— Jayshil Dave

2
Que tal em vista ter Actionbar, você não pode
— escurecê-

funcionou melhor para mim ... a resposta de pop-up duplo abaixo tinha uma condição de corrida, então o pop-up esmaecido às vezes ficava sobre o pop
— kenyee

1
Quase perfeito. A única desvantagem é que você tem que usar FrameLayout, pois não há método "getForeground" para outros layouts.
— LiangWang

81

Desde PopupWindowapenas adiciona um Viewpara WindowManagerque você pode usar updateViewLayout (View view, ViewGroup.LayoutParams params)para atualizar o LayoutParamsdos seus PopupWindow's contentViewdepois de chamar espectáculo .. ().

Definir o sinalizador da janela FLAG_DIM_BEHINDescurecerá tudo atrás da janela. Use dimAmountpara controlar a quantidade de não ofuscante (1,0 para opaco completamente a 0,0 para sem brilho).

Lembre-se de que se você definir um plano de fundo para seu, PopupWindowele o colocará contentViewem um contêiner, o que significa que você precisa atualizá-lo.

Com fundo:

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

Sem fundo:

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

Atualização do Marshmallow:

Em M PopupWindow envolve o contentView dentro de um FrameLayout chamado mDecorView. Se você pesquisar a fonte PopupWindow, encontrará algo como createDecorView(View contentView). O objetivo principal do mDecorView é lidar com o envio de eventos e as transições de conteúdo, que são novas para M. Isso significa que precisamos adicionar mais um .getParent () para acessar o contêiner.

Com antecedentes que exigiriam uma mudança para algo como:

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

Melhor alternativa para API 18+

Uma solução menos hacky usando ViewGroupOverlay:

1) Obtenha o layout de raiz desejado

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

2) Ligue applyDim(root, 0.5f);ouclearDim()

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
Isso parece não funcionar mais em dispositivos com Android 6.0 Marshmallow. O layoutParams não é lançado para WindowManager.LayoutParams. Existe uma solução alternativa?
— Robert

Obrigado por apontar isso. Eu não testei no M ainda. Irá atualizar o mais rápido possível.
— Markus Rubey

@ stackoverflow.com/users/308153/robert Eu também tive o mesmo problema. Usei o código acima com plano de fundo.
— Rizwan Sohaib

Se p for nulo, use: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
Para sua informação, quando eu uso isso em níveis de API mais baixos (por exemplo, 15 e 20), ele trava porque popup.getContentView (). GetParent () não retorna View, então lançá-lo causará um travamento. Para este caso, usei o método de @ BeytanKurt como fallback.
— Matthew Horst

30

Em seu arquivo xml, adicione algo assim com largura e altura como '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>

Em sua atividade ao criar

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

Finalmente, torne visível quando você mostrar sua janela pop-up e deixe-o visível quando você sair da janela pop-up.

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

1
Por favor, não deixe assinaturas como "Obrigado, Rupesh" nas postagens.
— Simon Hellinger,

3
mas isso não escurecerá a barra de ação / ferramenta.
— Silvia H

Por favor, diga-me como fazer a barra de ferramentas escurecer
— SAndroidD

1
adicionar back_dim_layout.setVisibility (View.GONE); em PopupWindow.OnDismissListener
— Pulkit

com certeza me ajudou
— Pixxu Waku

12

Outro truque é usar 2 janelas pop-up em vez de uma. A primeira janela pop-up será simplesmente uma visão fictícia com fundo translúcido que fornece o efeito de escurecimento. A segunda janela pop-up é a janela pop-up pretendida.

Sequência durante a criação de janelas pop-up: Mostre a janela pop-up fictícia primeiro e depois a janela pop-up pretendida.

Sequência durante a destruição: Ignore a janela pop-up pretendida e, em seguida, a janela pop-up fictícia.

A melhor maneira de vincular esses dois é adicionar um OnDismissListener e substituir o onDismiss()método do pretendido para apagar a janela popup fictícia deles.

Código para a janela pop-up fictícia:

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>

Mostrar pop-up de esmaecimento para escurecer o fundo

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

Quase funciona para mim ... às vezes, há uma condição de corrida em que a janela pop-up está atrás da janela pop-up esmaecida / fictícia. Ainda não encontrei uma solução alternativa ... mesmo atrasar a exibição da janela pop-up para dar um tempo atrás da janela atrasada não ajuda :-P
— kenyee

2
@kenyee Encontrei uma solução para esse problema. Eu mostro o pop-up 'fade' primeiro e, em seguida, para mostrar o segundo pop-up, estou usando myFadePopup.getContentView (). Post (... myPopup.showAtLocation ...);
— Piotr

5

Eu encontrei uma solução para isso

Crie uma caixa de diálogo transparente personalizada e, dentro dessa caixa de diálogo, abra a janela pop-up:

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 para a caixa de diálogo (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" />

agora você deseja descartar a caixa de diálogo quando a janela Popup for fechada. então

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

Nota: usei o NewQuickActionplugin para criar PopupWindow aqui. Também pode ser feito em janelas pop-up nativas


1

Para mim, algo como a resposta de Abdelhak Mouaamou funciona, testado na API de nível 16 e 27.

Em vez de usar popupWindow.getContentView().getParent()e lançar o resultado para View(que trava na API de nível 16 porque lá ele retorna um ViewRootImplobjeto que não é uma instância de View), eu apenas uso o .getRootView()que já retorna uma visualização, portanto, não é necessário lançar aqui.

Espero que ajude alguém :)

exemplo de trabalho completo misturado a partir de outras postagens stackoverflow, basta copiar e colar, por exemplo, no ouvinte onClick de um botão:

// 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 é o id do layout cujo brilho você deseja diminuir.


0

Você pode usar android:theme="@android:style/Theme.Dialog"para fazer isso. Crie uma atividade e em você AndroidManifest.xmldefina a atividade como:

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

Isso não parece funcionar. Apliquei isso à minha única atividade, que está em segundo plano quando mostro uma janela pop-up que é apenas um layout inflado que tenho, e não uma atividade separada. Como aplico FLAG_BLUR_BEHIND a uma PopupWindow?
— k_day

0

ok, então eu sigo a resposta do uhmdown para escurecer a atividade em segundo plano quando a janela pop está aberta. Mas isso cria problemas para mim. estava escurecendo a atividade e incluía janela pop-up (significa preto escuro em camadas na atividade e no pop-up também, não pode ser separado).

então eu tentei desta forma,

crie um arquivo dimming_black.xml para efeito de escurecimento,

 <?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 adicionar como backgroundem FrameLayoutcomo tag xml raiz, também colocar meus outros controles LinearLayoutcomo estelayout.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 o pop-up no meu MainActivitycom alguns parâmetros extras definidos como abaixo.

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

Resultado: insira a descrição da imagem aqui

funciona para mim, também resolveu problema como comentado pelo BaDo . Com isso Actionbartambém pode ser esmaecido.

PS, eu não estou dizendo que o uhmdown está errado. Aprendi a responder e tento evoluir para o meu problema. Também fiquei confuso se essa é uma boa maneira ou não.

Qualquer sugestão também é apreciada, desculpe pelo meu inglês ruim.


0

Talvez este repo ajude para você: BasePopup

Este é o meu repositório, que é usado para resolver vários problemas do PopupWindow.

No caso de usar a biblioteca, se você precisar desfocar o fundo, basta chamar setBlurBackgroundEnable(true).

Veja o wiki para mais detalhes. (Idioma em zh-cn)

BasePopup: wiki


-1

Este código funciona

        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);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.