46

Tengo DialogFragment para mostrar View como una ventana emergente. La ventana aparece siempre en el medio de la pantalla. ¿Hay alguna manera de establecer la posición de la ventana DialogFragment? Miré en el source code pero no pude encontrar nada todavía.Posición de DialogFragment en Android

Respuesta

77

intentar algo como esto:

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

u otros métodos para getDialog().getWindow().

asegúrese de establecer la posición después de llamar a set-content.

+0

gracias Steelight elevé tu respuesta. Aunque tengo una pregunta.¿Por qué cambias el WindowManager.LayoutParam p? setGravity() parece ser el truco para configurar la ventana en la parte superior. No parece haber ningún efecto adverso en LayoutParams cuando lo probé o cuando leo la documentación sobre setGravity. – flobacca

+0

La pregunta era "cómo establecer la posición", no asumí que quise decir "superior", así que di el ejemplo de establecer múltiples atributos para obtener el efecto deseado. – Steelight

+0

+1 porque esto me mostró la dirección correcta. Lo que finalmente funcionó para mí: http://stackoverflow.com/a/20419231/56285 – Jonik

3

Uso la clase PopupWindow para este propósito porque puede especificar la ubicación y el tamaño de la vista.

+13

PopupWindow no es un fragmento, por lo que este es un gran dolor de cabeza. –

4

getDialog().getWindow() no funcionará para DialogFragment como getWindow() devolverá nulo si la actividad de alojamiento no está visible, que no es si está escribiendo una aplicación basada en fragmentos. Obtendrá un NullPointerException cuando pruebe getAttributes().

Recomiendo la respuesta de Mobistry. Si ya tiene una clase DialogFragment, no es demasiado difícil cambiar. Simplemente reemplace el método onCreateDialog por uno que construya y devuelva una ventana emergente. Entonces debería poder reutilizar la Vista que suministra al AlertDialog.builder.setView(), y llamar al (PopupWindow object).showAtLocation().

67

Derecha, me golpeé la cabeza contra la pared durante una o dos horas con esto, antes de obtener finalmente DialogFragment posicionado como yo quería.

Estoy construyendo en Steelight's answer aquí. Este es el enfoque más simple y confiable que encontré.

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

Tenga en cuenta que params.width y params.softInputMode (utilizado en la respuesta de Steelight) son irrelevantes para esto.


A continuación se muestra un ejemplo más completo. Lo que realmente necesitaba era alinear un cuadro de diálogo "cuadro de confirmación" junto a una vista "fuente" o "principal", en mi caso un ImageButton.

Opté por usar DialogFragment, en lugar de cualquier Fragmento personalizado, porque le da características de "diálogo" de forma gratuita (cierre el diálogo cuando el usuario haga clic fuera de él, etc.).

Example ConfirmBox
Ejemplo ConfirmBox por encima de su ImageButton "fuente" (papelera)

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

Es bastante fácil de hacer lo anterior más de uso general mediante la adición de parámetros de constructor o setters como sea necesario. (Mi última ConfirmBox tiene un botón con el estilo (dentro de unos límites, etc.) cuyo texto y View.OnClickListener se puede personalizar en el código.)

+0

La mejor respuesta que encontré. –

+0

Chicos, rock - gracias - Tengo curiosidad, ** ¿por qué no puedo simplemente configurar esto en el XML del fragmento? ** ¿Qué es lo que no entiendo como un lamentable desarrollador de iOS? ¡gracias hombre! :) – Fattie

+0

Grt respuesta !! Estoy teniendo algún problema, en mi botón 'fuente' de escenario (debajo del cual se mostrará el fragmento de diálogo) cambian los cambios de posición en la orientación de pantalla, así que al cambiar la orientación el fragmento de diálogo no actualiza la posición según la posición de' fuente'. ¿Podría darme alguna pista sobre cómo hacer esto? – Dory

3

Es necesario reemplazar el método onResume() en su DialogFragment como siguiente:

@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(); 
} 
Cuestiones relacionadas