Valentin Kuhn Valentin Kuhn - 1 month ago 11
Android Question

Setting a window background behind AlertDialog removes dialog centering

I'm creating an AlertDialog with customized view and window background. Setting a ColorDrawable works as expected, but setting a BitmapDrawable from resources makes the dialog appear right at the top of the screen (instead of centered). (Note: I'm talking of the background behind the dialog (normally a transparent grey, not the dialog's background itself!)

Dialog background (@drawable/dialog_bg):

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners android:radius="10dp" />
</shape>


Dialog layout:

<?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:background="@drawable/dialog_bg"
android:orientation="vertical">

<!-- dialog contents -->

</LinearLayout>


Code to show dialog with ColorDrawable: -> works

private void showDialog() {
final AlertDialog dialog;
@SuppressLint("InflateParams") final ViewGroup dialogView = (ViewGroup) activity.getLayoutInflater().inflate(R.layout.my_dialog, null);
dialog = new AlertDialog.Builder(activity).setView(dialogView).create();
// this works:
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.show();
}


Code to show dialog with BitmapDrawable from resources (loading a simple PNG): -> removes centering

private void showDialog() {
final AlertDialog dialog;
@SuppressLint("InflateParams") final ViewGroup dialogView = (ViewGroup) activity.getLayoutInflater().inflate(R.layout.my_dialog, null);
dialog = new AlertDialog.Builder(activity).setView(dialogView).create();
// this sets the background, but un-centers the dialog:
BitmapDrawable drawable = (BitmapDrawable) ResourcesCompat.getDrawable(activity.getResources(), R.drawable.my_bg, null);
dialog.getWindow().setBackgroundDrawable(drawable);
dialog.show();
}


Setting a ColorDrawable works as expected: The background behind the Dialog is colored and the dialog is still centered on screen.
Setting a BitmapDrawable does not work: The background is set but the dialog is moved to the top of the screen.

Things that also didn't work:


  • loading the drawable with
    ContextCompat.getDrawable()
    (which is the same as
    ResourcesCompat.getDrawable()
    with the current theme instead of null)

  • using DisplayMetrics and
    dialog.getWindow().getAttributes().y
    (and
    .x
    respectively) to calculate margins myself:
    (height - y) / 2
    -> just returns the "normal" dialog margin

  • setting the gravity to CENTER on either
    dialog.getWindow().setGravity()
    or
    dialog.getWindow().getAttributes().gravity
    -> this just doesn't change anything

  • setting the gravity to FILL on either
    dialog.getWindow().setGravity()
    or
    dialog.getWindow().getAttributes().gravity
    -> this removes dialog margins, but still at the top (even further at the top and left, as margins are removed)



So, does anybody know how to set a background from PNG behind the dialog and keeping its centering on the screen?

Answer Source

So I just solved this issue, although I have to admit it's a bit hacky.


First, I disabled fading behind the dialog

dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

and manually "faded" the activity by adding a view to it, overlaying the activity with semi-transparent black:

final ViewGroup dimBackgroundView = new FrameLayout(activity);
float dimAlpha = 0.5f;
dimBackgroundView.setBackgroundColor(Color.BLACK);
dimBackgroundView.setAlpha(dimAlpha);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
activityLayout.addView(dimBackgroundView, params);

This also requires me to manually darken the statusbar on supporting devices (SDK 21+):

final int statusBarColor;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    statusBarColor = activity.getWindow().getStatusBarColor();
    activity.getWindow().setStatusBarColor(
            Color.rgb((int) (Color.red(statusBarColor) + 255 * dimAlpha),
                    (int) (Color.green(statusBarColor) + 255 * dimAlpha),
                    (int) (Color.blue(statusBarColor) + 255 * dimAlpha)));
} else {
    statusBarColor = Color.BLACK;
}

Afterwards, I added the intended background to the activity (dialogBgView on top of the semi-transparent black view) and went on adding the dialog as normal. Since I now added all these views to the activity, I need to remove them on dialog dismissal:

dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
    @Override
    public void onDismiss(DialogInterface dialogInterface) {
        // remove dim
        activityLayout.removeView(dimBackgroundView);
        // restore original statusbar color
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.getWindow().setStatusBarColor(statusBarColor);
        }
        // remove background image
        activityLayout.removeView(dialogBgView);
    }
});

It works, but it's really not a nice solution. So if anyone discovers a better way, please feel free to post it here.