Làm mờ hoặc mờ nền khi Android PopupWindow hoạt động


85

Tôi muốn có thể làm mờ hoặc làm mờ nền khi tôi hiển thị cửa sổ bật lên của mình bằng cách sử dụng popup.showAtLocationvà bỏ làm mờ / làm mờ nền khi popup.dismissđược gọi.

Tôi đã thử áp dụng các thông số bố cục FLAG_BLUR_BEHINDFLAG_DIM_BEHINDcho hoạt động của mình, nhưng điều này dường như chỉ làm mờ và làm mờ nền ngay khi ứng dụng của tôi được khởi động.

Làm cách nào để làm mờ / làm mờ chỉ với cửa sổ bật lên?


1
Lời cảnh báo: FLAG_BLUR_BEHIND đang gặp lỗi trên một số điện thoại và khiến điện thoại rất không phản hồi (rõ ràng là phần mềm bị mờ được thực hiện). Ví dụ trên Droid. Ngoài ra, những gì Macarse đã nói - FLAG_BLUR_BEHIND cần được áp dụng cho cửa sổ ở nền trước.
EboMike


1
Tôi đang tìm kiếm câu trả lời tương tự cho câu hỏi này. Có cách nào đó để tôi có thể lập trình làm mờ chế độ xem đằng sau cửa sổ bật lên không?
Nick

1
@Nick Làm thế nào bạn giải quyết vấn đề này ..
Amit

Câu trả lời:


106

Câu hỏi là về Popupwindowlớp học, nhưng mọi người đã đưa ra câu trả lời sử dụng Dialoglớp học. Đó là khá nhiều vô ích nếu bạn cần sử dụng Popupwindowlớp, vì Popupwindowkhông có getWindow()phương thức.

Tôi đã tìm thấy một giải pháp thực sự hiệu quả Popupwindow. Nó chỉ yêu cầu gốc của tệp xml mà bạn sử dụng cho hoạt động nền là a FrameLayout. Bạn có thể cung cấp cho Framelayoutphần tử một android:foregroundthẻ. Những gì thẻ này làm là chỉ định một tài nguyên có thể kéo được sẽ được xếp lớp trên toàn bộ hoạt động (nghĩa là nếu Framelayout là phần tử gốc trong tệp xml). Sau đó, bạn có thể kiểm soát độ mờ ( setAlpha()) của nền trước có thể vẽ được.

Bạn có thể sử dụng bất kỳ tài nguyên có thể vẽ nào mà bạn thích, nhưng nếu bạn chỉ muốn có hiệu ứng làm mờ, hãy tạo tệp xml trong thư mục có thể vẽ với <shape>thẻ là 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>

(Xem http://developer.android.com/guide/topics/resources/drawable-resource.html#Shape để biết thêm thông tin về shapephần tử). Lưu ý rằng tôi đã không chỉ định giá trị alpha trong thẻ màu để làm cho mục có thể vẽ trong suốt (ví dụ #ff000000:). Lý do cho điều này là bất kỳ giá trị alpha được mã hóa cứng nào dường như sẽ ghi đè bất kỳ giá trị alpha mới nào mà chúng tôi đặt thông qua setAlpha()mã của mình, vì vậy chúng tôi không muốn điều đó. Tuy nhiên, điều đó có nghĩa là vật có thể vẽ được ban đầu sẽ không trong suốt (rắn, không trong suốt). Vì vậy, chúng ta cần phải minh bạch hóa nó trong onCreate()phương thức của hoạt động .

Đây là mã phần tử 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>

Đây là phương thức onCreate () của 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);
}

Cuối cùng, mã để làm mờ hoạt động:

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

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

Các giá trị alpha đi từ 0(mờ) đến 255(không nhìn thấy). Bạn nên bỏ làm mờ hoạt động khi bạn loại bỏ Cửa sổ bật lên.

Tôi chưa bao gồm mã để hiển thị và loại bỏ Popupwindow, nhưng đây là liên kết để thực hiện nó: http://www.mobilemancer.com/2011/01/08/popup-window-in-android/


để đảm bảo rằng tính năng này hoạt động trên các thiết bị cũ, bạn cũng phải vô hiệu hóa nền trước sau mỗi lần thay đổi kênh alpha của nó. Tôi đã thêm điều này vào câu trả lời. +1 cho câu trả lời hay
dùng2224350 28/12/13

1
Tôi không thể nghĩ ra một lý do mà đây không được đánh dấu là câu trả lời đúng .. câu trả lời tuyệt vời để nói rằng ít nhất.
Jayshil Dave,

2
Còn trong chế độ xem có Thanh hành động thì sao, Bạn không thể làm mờ nó
BaDo

này làm việc tốt nhất cho tôi ... popup câu trả lời đôi dưới đây đã có một tình trạng chủng tộc nên mờ cửa sổ bật lên đôi khi đã kết thúc thực sổ popup
kenyee

1
Gân như hoan hảo. Hạn chế duy nhất là bạn phải sử dụng FrameLayout vì không có phương thức "getForeground" cho các bố cục khác.
LiangWang

79

Kể từ khi PopupWindowchỉ cần thêm một Viewđể WindowManagerbạn có thể sử dụng updateViewLayout (View view, ViewGroup.LayoutParams params)để cập nhật các LayoutParamscủa bạn PopupWindow'scontentView sau khi gọi chương trình .. ().

Đặt cờ cửa sổ FLAG_DIM_BEHINDsẽ làm mờ mọi thứ phía sau cửa sổ. Sử dụng dimAmountđể kiểm soát lượng mờ (1,0 cho mờ hoàn toàn đến 0,0 cho không mờ).

Hãy nhớ rằng nếu bạn đặt nền cho nền, PopupWindownó sẽ đưa bạn contentViewvào một vùng chứa, có nghĩa là bạn cần cập nhật nền đó.

Với nền:

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

Không có nền:

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

Cập nhật Marshmallow:

Trên M PopupWindow bao bọc contentView bên trong FrameLayout được gọi là mDecorView. Nếu bạn tìm hiểu kỹ nguồn PopupWindow, bạn sẽ thấy một cái gì đó nhưcreateDecorView(View contentView) .

Với nền sẽ yêu cầu thay đổi một cái gì đó như:

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

Thay thế tốt hơn cho API 18+

Một giải pháp ít hack hơn bằng cách sử dụng ViewGroupOverlay:

1) Nắm giữ bố cục gốc mong muốn

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

2) Gọi applyDim(root, 0.5f);hoặcclearDim()

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
Điều này dường như không còn hoạt động trên các thiết bị chạy Android 6.0 Marshmallow. LayoutParams không truyền tới WindowManager.LayoutParams. Có một giải pháp thay thế?
Robert

Cảm ơn đã chỉ ra điều đó. Tôi chưa thử nghiệm nó trên M. Sẽ cập nhật càng sớm càng tốt.
Markus Rubey

@ stackoverflow.com/users/308153/robert Tôi cũng gặp vấn đề tương tự. Tôi đã sử dụng mã trên với nền.
Rizwan Sohaib

Nếu p là null sử dụng này: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 Khi tôi sử dụng điều này ở các cấp API thấp hơn (ví dụ: 15 và 20), nó bị treo vì popup.getContentView (). GetParent () không trả về Chế độ xem nên việc truyền nó sẽ gây ra sự cố. Đối với trường hợp này, tôi đã sử dụng phương pháp của @ BeytanKurt làm phương pháp dự phòng.
Matthew Horst

29

Trong tệp xml của bạn, hãy thêm một cái gì đó như thế này với chiều rộng và chiều cao là '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>

Trong hoạt động của bạn oncreate

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

Cuối cùng, hiển thị khi bạn hiển thị cửa sổ bật lên của mình và hiển thị nó biến mất khi bạn thoát khỏi cửa sổ bật lên.

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

1
Vui lòng không để lại chữ ký như "Cảm ơn, Rupesh" trong các bài viết.
Simon Hellinger

3
nhưng điều này sẽ không làm mờ thanh hành động / công cụ.
Silvia H

Plz cho tôi biết làm thế nào để hành động toolbar mờ
SAndroidD

thêm back_dim_layout.setVisibility (View.GONE); trong PopupWindow.OnDismissListener
Pulkit

chắc chắn đã giúp tôi
Pixxu Waku 22/02/19

12

Một thủ thuật khác là sử dụng 2 cửa sổ bật lên thay vì một. Cửa sổ bật lên đầu tiên sẽ chỉ đơn giản là một chế độ xem giả với nền mờ tạo hiệu ứng mờ. Cửa sổ bật lên thứ 2 là cửa sổ bật lên dự định của bạn.

Trình tự trong khi tạo cửa sổ bật lên: Hiển thị cửa sổ bật lên giả đầu tiên và sau đó là cửa sổ bật lên dự định.

Trình tự trong khi phá hủy: Bỏ qua cửa sổ bật lên dự định và sau đó là cửa sổ bật lên giả.

Cách tốt nhất để liên kết hai thứ này là thêm OnDismissListener và ghi đè onDismiss()phương thức dự định loại bỏ cửa sổ bật lên giả khỏi cửa sổ bật lên giả của chúng.

Mã cho cửa sổ bật lên giả:

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>

Hiển thị cửa sổ bật lên mờ dần để làm mờ nền

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

Hầu như hoạt động đối với tôi ... đôi khi, có một điều kiện chạy đua trong đó cửa sổ bật lên ở phía sau cửa sổ bật lên mờ / giả. Vẫn chưa tìm ra cách giải quyết ... thậm chí trì hoãn việc hiển thị cửa sổ bật lên để cho nó có thời gian đằng sau cái bị trì hoãn cũng không giúp được gì :-P
kenyee

2
@kenyee Tôi đã tìm thấy bản sửa lỗi cho vấn đề này. Tôi hiển thị cửa sổ bật lên 'mờ dần' đầu tiên, sau đó để hiển thị cửa sổ bật lên thứ hai, tôi đang sử dụng myFadePopup.getContentView (). Post (... myPopup.showAtLocation ...);
Piotr

5

Tôi đã tìm thấy một giải pháp cho điều này

Tạo một hộp thoại trong suốt tùy chỉnh và bên trong hộp thoại đó, hãy mở cửa sổ bật lên:

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 cho hộp thoại (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" />

bây giờ bạn muốn loại bỏ hộp thoại khi cửa sổ bật lên loại bỏ. vì thế

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

Lưu ý: Tôi đã sử dụng NewQuickActionplugin để tạo PopupWindow tại đây. Nó cũng có thể được thực hiện trên Windows Popup gốc


1

Đối với tôi, thứ gì đó giống như câu trả lời của Abdelhak Mouaamou hoạt động, được thử nghiệm trên API cấp 16 và 27.

Thay vì sử dụng popupWindow.getContentView().getParent()và truyền kết quả đến View(mà sự cố trên API cấp 16 gây ra ở đó nó trả về một ViewRootImplđối tượng không phải là một phiên bản của View), tôi chỉ sử dụng .getRootView()mà trả về một chế độ xem đã có, vì vậy không cần truyền ở đó.

Hy vọng nó sẽ giúp ai đó :)

hoàn chỉnh ví dụ làm việc được xáo trộn với nhau từ các bài đăng stackoverflow khác, chỉ cần sao chép và dán nó, ví dụ: trong trình nghe onClick của một nút:

// 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 của bố cục mà bạn muốn giảm độ sáng.


0

Bạn có thể sử dụng android:theme="@android:style/Theme.Dialog"để làm điều đó. Tạo một hoạt động và trong bạn AndroidManifest.xmlxác định hoạt động đó là:

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

Điều này dường như không phải là thủ thuật. Tôi đã áp dụng điều này cho hoạt động duy nhất của mình, hoạt động này ở chế độ nền khi tôi hiển thị cửa sổ bật lên, đó chỉ là một bố cục tăng cao mà tôi có chứ không phải là một hoạt động riêng biệt. Làm cách nào để áp dụng FLAG_BLUR_BEHIND cho Cửa sổ bật lên?
k_day,

0

ok, vì vậy tôi làm theo câu trả lời của uhmdown cho hoạt động nền mờ khi cửa sổ bật mở đang mở. Nhưng nó tạo ra vấn đề cho tôi. đó là hoạt động mờ và bao gồm cửa sổ bật lên (có nghĩa là lớp đen mờ được phân lớp trên cả hoạt động và cửa sổ bật lên, không thể tách chúng ra).

vì vậy tôi đã thử theo cách này,

tạo tệp dimming_black.xml để có hiệu ứng làm mờ,

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

Và thêm backgroundvào FrameLayoutdưới dạng thẻ xml gốc, cũng đưa các điều khiển khác của tôi vào LinearLayoutnhư thế nàylayout.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>

cuối cùng tôi hiển thị cửa sổ bật lên của tôi MainActivityvới một số thông số bổ sung được đặt như bên dưới.

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

Kết quả: nhập mô tả hình ảnh ở đây

nó hoạt động cho tôi, cũng được giải quyết vấn đề như nhận xét của BaDo . Với điều này Actionbarcũng có thể được làm mờ.

Ps, tôi không nói uhmdown's là sai. tôi đã học được mẫu câu trả lời của anh ấy và cố gắng phát triển cho vấn đề của mình. Tôi cũng bối rối không biết đây có phải là một cách tốt hay không.

Bất kỳ góp ý cũng được đánh giá cao cũng xin lỗi cho tiếng Anh xấu của tôi.


0

Có lẽ repo này sẽ giúp cho bạn: BasePopup

Đây là repo của tôi, được sử dụng để giải quyết các vấn đề khác nhau của PopupWindow.

Trong trường hợp sử dụng thư viện, nếu bạn cần làm mờ nền, chỉ cần gọi setBlurBackgroundEnable(true).

Xem wiki để biết thêm chi tiết. (Ngôn ngữ trong zh-cn)

BasePopup: wiki


-1

Mã này hoạt động

        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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.