70

I have a DialogFragment to show a View like a Popup screen. The Window appears always in the middle of the screen. Is there a way to set the position of the DialogFragment window? I have looked in to the source code but couldn't find anything yet.

Jonik
  • 80,077
  • 70
  • 264
  • 372
alfacan
  • 715
  • 1
  • 7
  • 11

8 Answers8

112

Try something like this:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    getDialog().getWindow().setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
    WindowManager.LayoutParams p = getDialog().getWindow().getAttributes();
    p.width = ViewGroup.LayoutParams.MATCH_PARENT;
    p.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
    p.x = 200;
    ...
    getDialog().getWindow().setAttributes(p);
    ...

or other methods for getDialog().getWindow().

be sure to set the position after calling set-content.

Youngjae
  • 24,352
  • 18
  • 113
  • 198
Steelight
  • 3,347
  • 1
  • 20
  • 17
  • thanks Steelight I upvoted your answer. I do have a question though. Why are you changing the WindowManager.LayoutParam p? setGravity() seems to do the trick for setting the window at the top. There doesn't seem to be any adverse effects on the LayoutParams when I tested it or when I read the documentation about the setGravity. – flobacca Jul 15 '13 at 05:32
  • The question was "how to set the position", I did not assume he meant 'top', so I gave the example of setting multiple attributes to get any desired effect. – Steelight Jul 15 '13 at 07:06
  • 1
    +1 because this showed me the right direction. What ultimately worked for me: http://stackoverflow.com/a/20419231/56285 – Jonik Dec 06 '13 at 09:13
  • Note: The Dialog will float behind the System Navigation buttons on Android 22 and below, by someone @Kyle R but lack enough reputation to comment. – Xenolion Feb 20 '18 at 22:24
  • Thanks. P.y worked for me to change vertical positioning of dialog. and i used this code in onStart() which i think perfect place for it. – Khizar Hayat Feb 20 '19 at 10:53
104

Right, I banged head against wall for an hour or two with this, before finally getting DialogFragment positioned like I wanted.

I'm building on Steelight's answer here. This is the simplest, most reliable approach I found.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle b) {
    Window window = getDialog().getWindow();

    // set "origin" to top left corner, so to speak
    window.setGravity(Gravity.TOP|Gravity.LEFT);

    // after that, setting values for x and y works "naturally"
    WindowManager.LayoutParams params = window.getAttributes();
    params.x = 300;
    params.y = 100;
    window.setAttributes(params);

    Log.d(TAG, String.format("Positioning DialogFragment to: x %d; y %d", params.x, params.y));
} 

Note that params.width and params.softInputMode (used in Steelight's answer) are irrelevant for this.


Below is a more complete example. What I really needed was to align a "confirm box" DialogFragment next to a "source" or "parent" View, in my case an ImageButton.

I chose to use DialogFragment, instead of any custom Fragment, because it gives you "dialog" features for free (close dialog when user clicks outside of it, etc).

Example ConfirmBox
Example ConfirmBox above its "source" ImageButton (trashcan)

/**
 * A custom DialogFragment that is positioned above given "source" component.
 *
 * @author Jonik, https://stackoverflow.com/a/20419231/56285
 */
public class ConfirmBox extends DialogFragment {
    private View source;

    public ConfirmBox() {
    }

    public ConfirmBox(View source) {
        this.source = source;            
    }

    public static ConfirmBox newInstance(View source) {
        return new ConfirmBox(source);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setStyle(STYLE_NO_FRAME, R.style.Dialog);
    }


    @Override
    public void onStart() {
        super.onStart();

        // Less dimmed background; see https://stackoverflow.com/q/13822842/56285
        Window window = getDialog().getWindow();
        WindowManager.LayoutParams params = window.getAttributes();
        params.dimAmount = 0.2f; // dim only a little bit
        window.setAttributes(params);

        // Transparent background; see https://stackoverflow.com/q/15007272/56285
        // (Needed to make dialog's alpha shadow look good)
        window.setBackgroundDrawableResource(android.R.color.transparent);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Put your dialog layout in R.layout.view_confirm_box
        View view = inflater.inflate(R.layout.view_confirm_box, container, false);

        // Initialise what you need; set e.g. button texts and listeners, etc.

        // ...

        setDialogPosition();

        return view;
    }

    /**
     * Try to position this dialog next to "source" view
     */
    private void setDialogPosition() {
        if (source == null) {
            return; // Leave the dialog in default position
        }

        // Find out location of source component on screen
        // see https://stackoverflow.com/a/6798093/56285
        int[] location = new int[2];
        source.getLocationOnScreen(location);
        int sourceX = location[0];
        int sourceY = location[1];

        Window window = getDialog().getWindow();

        // set "origin" to top left corner
        window.setGravity(Gravity.TOP|Gravity.LEFT);

        WindowManager.LayoutParams params = window.getAttributes();

        // Just an example; edit to suit your needs.
        params.x = sourceX - dpToPx(110); // about half of confirm button size left of source view
        params.y = sourceY - dpToPx(80); // above source view

        window.setAttributes(params);
    }

    public int dpToPx(float valueInDp) {
        DisplayMetrics metrics = getActivity().getResources().getDisplayMetrics();
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
    }
}

It's quite easy to make the above more general-use by adding constructor parameters or setters as necessary. (My final ConfirmBox has a styled button (inside some borders etc) whose text and View.OnClickListener can be customised in code.)

Community
  • 1
  • 1
Jonik
  • 80,077
  • 70
  • 264
  • 372
  • 1
    Awesome best answer i found. – Aiden Fry Feb 26 '14 at 14:36
  • 2
    Guys you rock -- thanks -- I'm curious, **why can't I just set this in the XML of the fragment?** What is it I don't understand as a lame iOS dev? thanks men! :) – Fattie May 24 '14 at 16:47
  • Grt answer !! I am having any problem,in my scenario `source` button (below which the dialog fragment is to be shown) position changes on screen orientation change,so on changing the orientation the dialog fragment is not updating the position as per the position of `source`. Could you please give me some hint on how to do this. – Dory Sep 16 '14 at 12:30
  • @alfacan This one should be the accepted answer... Using alternative may not be acceptable always. – Evren Ozturk Oct 27 '14 at 07:20
  • Nice solution, but question : what can i do if i want the dialog to fill all the space under the view ? – An-droid Mar 02 '15 at 15:03
  • 1
    @Jonik men, you are awesome. your answer, your profile, your reputation,..... all awesomeee. – Edijae Crusar Mar 24 '16 at 11:25
6

I use AppCompatDialogFragment from android.support.v7.app.AppCompatDialogFragment and I want to align dialog fragment to bottom of the screen and also remove all borders, especially I needed to set content width to match parent.

So, I wanted from this (yellow background comes from rootLayout of dialog fragment):

src_img_1

Get this:

src_img_2

None of solutions above worked. So, I did this:

fun AppCompatDialogFragment.alignToBottom() {
    dialog.window.apply {
        setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
        decorView.apply {

            // Get screen width
            val displayMetrics = DisplayMetrics().apply {
                windowManager.defaultDisplay.getMetrics(this)
            }

            setBackgroundColor(Color.WHITE) // I don't know why it is required, without it background of rootView is ignored (is transparent even if set in xml/runtime)
            minimumWidth = displayMetrics.widthPixels
            setPadding(0, 0, 0, 0)
            layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
            invalidate()
        }
    }
}
Cililing
  • 4,303
  • 1
  • 17
  • 35
  • In my case, to make full width dialog `dialog?.window?.setLayout(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT)` was required. – Mikhail Sharin Dec 23 '21 at 13:54
5

You need to override onResume() method in your DialogFragment like following:

@Override
public void onResume() {
    final Window dialogWindow = getDialog().getWindow();
    WindowManager.LayoutParams lp = dialogWindow.getAttributes();
    lp.x = 100;        // set your X position here
    lp.y = 200;        // set your Y position here
    dialogWindow.setAttributes(lp);

    super.onResume();
}
  • 2
    The key to making this work is to set an origin. For example: Window window = getDialog().getWindow(); // set "origin" to top left corner, so to speak window.setGravity(Gravity.TOP|Gravity.LEFT); If you do not set the origin then the x and y positions will be relative to the center of the screen. – Kenneth Argo Jan 23 '19 at 05:59
4

getDialog().getWindow() will not work for a DialogFragment as getWindow() will return null if the hosting activity is not visible, which it isn't if you're writing a fragment-based app. You will get a NullPointerException when you try getAttributes().

I recommend Mobistry's answer. If you already have a DialogFragment class, it's not too hard to switch over. Just replace the onCreateDialog method with one that constructs and returns a PopupWindow. Then you should be able to reuse the View you supply to AlertDialog.builder.setView(), and call (PopupWindow object).showAtLocation().

gprathour
  • 14,813
  • 5
  • 66
  • 90
Austin B
  • 1,590
  • 1
  • 13
  • 25
4

In case somebody would like to bring the dialog to the screen's bottom, and make it full width, here is my solution:

    @Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    // the content
    final RelativeLayout root = new RelativeLayout(getActivity());
    root.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

    // creating the fullscreen dialog
    final Dialog dialog = new Dialog(getActivity());
    Window dialogWindow = dialog.getWindow();

    dialog.setContentView(root);
    dialogWindow.setBackgroundDrawable(new ColorDrawable(Color.WHITE));

    WindowManager.LayoutParams params = dialogWindow.getAttributes();

    params.width = ViewGroup.LayoutParams.MATCH_PARENT;
    params.height = ViewGroup.LayoutParams.WRAP_CONTENT;

    dialogWindow.setAttributes(params);
    dialogWindow.setGravity(Gravity.BOTTOM);


    return dialog;
}
Amir Golan
  • 378
  • 3
  • 8
2

I am using PopupWindow class for this purpose because you can specify the location and size of the view.

Sileria
  • 15,223
  • 4
  • 49
  • 28
2
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    Dialog dialog = new Dialog(mActivity, R.style.BottomDialog);
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); // 
    dialog.setContentView(R.layout.layout_video_live);
    dialog.setCanceledOnTouchOutside(true);

    Window window = dialog.getWindow();
    assert window != null;
    WindowManager.LayoutParams lp = window.getAttributes();
    lp.gravity = Gravity.BOTTOM; //psotion
    lp.width = WindowManager.LayoutParams.MATCH_PARENT; // fuill screen
    lp.height = 280;
    window.setAttributes(lp);
    return dialog;
}
Ven Ren
  • 1,584
  • 1
  • 13
  • 24