Cómo implementar una vista de AlertDialog personalizada

107

En los documentos de Android en AlertDialog , proporciona la siguiente instrucción y ejemplo para configurar una vista personalizada en un AlertDialog:

Si desea mostrar una vista más compleja, busque el FrameLayout llamado "cuerpo" y agregue su vista:

FrameLayout fl = (FrameLayout) findViewById(R.id.body);
fl.add(myView, new LayoutParams(FILL_PARENT, WRAP_CONTENT));

En primer lugar, es bastante obvio que add()es un error tipográfico y está destinado a serlo addView().

Estoy confundido por la primera línea que usa R.id.body. Parece que es el elemento del cuerpo del AlertDialog ... pero no puedo simplemente ingresar eso en mi código porque da un error de compilación. ¿Dónde se define o asigna R.id.body o lo que sea?

Aquí está mi código. Traté de usar setView(findViewById(R.layout.whatever)en el constructor pero no funcionó. ¿Estoy asumiendo porque no lo inflé manualmente?

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Title")
    .setCancelable(false)
    .setPositiveButton("Go", new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int id) {
        EditText textBox = (EditText) findViewById(R.id.textbox);
        doStuff();
    }
});

FrameLayout f1 = (FrameLayout)findViewById(R.id.body /*CURRENTLY an ERROR*/);
f1.addView(findViewById(R.layout.dialog_view));

AlertDialog alert = builder.create();
alert.show();
stormin986
fuente
Para encontrar y usar sus objetos en un diálogo, siga estos cuatro pasos: stackoverflow.com/a/18773261/1699586
Sara
3
Respuesta de una línea: agregue .setView(getLayoutInflater().inflate(R.layout.dialog_view, null))al constructor. Crédito a Sergio Viudes, a continuación.
1 ''

Respuestas:

49

Tienes razón, es porque no lo inflaste manualmente. Parece que estás intentando "extraer" la identificación del "cuerpo" del diseño de tu actividad, y eso no funcionará.

Probablemente quieras algo como esto:

LayoutInflater inflater = getLayoutInflater();
FrameLayout f1 = (FrameLayout)alert.findViewById(android.R.id.body);
f1.addView(inflater.inflate(R.layout.dialog_view, f1, false));
synic
fuente
17
Curiosamente, el cuerpo no se define como una constante en android.R.id. Todavía no tengo claro cómo acceder al elemento 'cuerpo' del AlertDialog creado. Todavía me gustaría saber cómo hacer esto, pero por ahora solo intentaré inflar una vista y usar setView en el constructor.
stormin986
2
En realidad, esto todavía me deja con una pregunta (soy nuevo en inflar vistas). Usando builder.setView(inflater.inflate(R.id.dialog, ROOT_VIEWGROUP[, ATTACH_TO_ROOT])), los documentos dicen que el grupo de vistas raíz es opcional. ¿Debería utilizarse en este caso? Si es así ... todavía tengo que averiguar cómo obtener una referencia al AlertDialog ...
stormin986
2
Es opcional, pero luego no tendrá una referencia al padre desde dentro del diseño que está inflando. Cosas como android: layout_gravity no funcionarán en la vista de nivel superior ... y tal vez no los necesite. Cuando llama a AlertDialog alert = builder.create (), tiene una referencia a su AlertDialog. Respuesta larga corta, es opcional. Pruébelo, dependiendo de lo que esté haciendo en su diseño personalizado, probablemente funcionará.
Synic
2
No tengo claro cómo hacer referencia a la vista dentro de AlertDialog. ¿Qué recomendaría hacer en este caso si quisiera hacer referencia al padre? Lo único que veo dentro de alertDialog que devuelve una vista es getCurrentFocus ()
stormin986
5
Sostenga el Viewinflado. Llamar findViewById()en que Viewcuando se necesita cosas de su contenido. Ver: github.com/commonsguy/cw-android/tree/master/Database/Constants
CommonsWare
159

Puede crear su vista directamente desde Layout Inflater, solo necesita usar el nombre de su archivo XML de diseño y el ID del diseño en el archivo.

Su archivo XML debe tener una identificación como esta:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/dialog_layout_root"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:padding="10dp"
              />

Y luego puede configurar su diseño en el constructor con el siguiente código:

LayoutInflater inflater = getLayoutInflater();
View dialoglayout = inflater.inflate(R.layout.dialog_layout, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(dialoglayout);
builder.show();
Andrewx2
fuente
4
¿Y dónde está R.id.dialog_layout_root en este ejemplo? ¿No es una vista de la actividad actual?
Alex Pretzlav
5
@AlexPretzlav: dialog_layout_root no es necesario en este ejemplo. Todo lo que necesita es el nombre de su archivo xml para R.layout. [Name_of_xml_file].
IgorGanapolsky
7
@Temperage ¿Agregaste builder.show al final? Probé este código y funcionó. También pasé null como segundo parámetro para infalter.inflate
Vinoth
2
Esto debería haber sido seleccionado como la mejor respuesta.
Salman Khakwani
2
Esto da una ClassCastException cuando el diseño personalizado contiene un EditText, porque getCurrentFocus()devolverá el EditText y un EditText no se puede convertir en un ViewGroup. El uso nullcomo segundo argumento soluciona esto.
1 ''
20

android.R.id.custom devolvía nulo para mí. Me las arreglé para que esto funcione en caso de que alguien se encuentre con el mismo problema,

AlertDialog.Builder builder = new AlertDialog.Builder(context)
            .setTitle("My title")
            .setMessage("Enter password");
final FrameLayout frameView = new FrameLayout(context);
builder.setView(frameView);

final AlertDialog alertDialog = builder.create();
LayoutInflater inflater = alertDialog.getLayoutInflater();
View dialoglayout = inflater.inflate(R.layout.simple_password, frameView);
alertDialog.show();

Como referencia, R.layout.simple_password es:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:layout_width="match_parent"
          android:layout_height="match_parent">

<EditText
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/password_edit_view"
        android:inputType="textPassword"/>
<CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/show_password"
        android:id="@+id/show_password_checkbox"
        android:layout_gravity="left|center_vertical"
        android:checked="false"/>

</LinearLayout>
John Rellis
fuente
John Rellis. Tu solución funciona bien. Solo ten algo en él. Deberíamos inflar antes de builder.create y establecer frameView.setLayoutParams (new LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); o algo primero, que evitará algunos errores que no se esperaban
MOBYTELAB
gracias por los comentarios ... ¿cómo se infla antes de builder.create ()? inflar se llama en el inflador del diálogo y solo tengo un diálogo porque he llamado builder.create ()
John Rellis
podemos usar el inflador de actividad, y no adjuntarlo a FrameLayout, por lo que podemos reducir un pequeño código como este. AlertDialog.Builder builder = new AlertDialog.Builder (new ContextThemeWrapper (getActivity (), android.R.style.Theme_Holo)) .setTitle ("título") .setIcon (R.drawable.sample_icon); Ver problemView = inflater.inflate (R.layout.sample_layout, null, false); builder.setView (problemView); alerta = constructor.create (); Lo siento, no sé cómo escribir código claramente en el comentario
MOBYTELAB
Se trata solo de un error de diseño, si reunimos todo en el diseño xml y nos olvidamos de Framelayout como raíz del diálogo, podemos tener curiosidad si el diseño en xml no es el padre de relleno o algo así, solo un caso.
MOBYTELAB
¡Funcionó para mí, gracias! Este me permite agregar un título y mi botón Cancelar y Aceptar, incluido EditarTexto sin error. :) La única pregunta es, ¿cómo funciona esto? ¿ FrameLayoutActúa como una especie de fragmento? Cuando probé la respuesta de Andrewx2 anterior, recibí un error porque pensé que estaba inflando dos diseños (es mi suposición).
Azurespot
12

Esto funcionó para mí:

dialog.setView(dialog.getLayoutInflater().inflate(R.layout.custom_dialog_layout, null));
Sergio Viudes
fuente
8

Diálogo de alerta personalizado

Este ejemplo completo incluye la transferencia de datos a la actividad.

ingrese la descripción de la imagen aquí

Crea un diseño personalizado

Se EditTextusa un diseño con un para este ejemplo simple, pero puede reemplazarlo con lo que quiera.

custom_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:paddingLeft="20dp"
              android:paddingRight="20dp"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

Usar el diálogo en código

Las partes clave son

  • usando setViewpara asignar el diseño personalizado a laAlertDialog.Builder
  • enviar cualquier dato a la actividad cuando se hace clic en un botón de diálogo.

Este es el código completo del proyecto de ejemplo que se muestra en la imagen de arriba:

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void showAlertDialogButtonClicked(View view) {

        // create an alert builder
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Name");

        // set the custom layout
        final View customLayout = getLayoutInflater().inflate(R.layout.custom_layout, null);
        builder.setView(customLayout);

        // add a button
        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // send data from the AlertDialog to the Activity
                EditText editText = customLayout.findViewById(R.id.editText);
                sendDialogDataToActivity(editText.getText().toString());
            }
        });

        // create and show the alert dialog
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    // do something with the data coming from the AlertDialog
    private void sendDialogDataToActivity(String data) {
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
    }
}

Notas

  • Si se encuentra usando esto en varios lugares, considere crear una DialogFragmentsubclase como se describe en la documentación .

Ver también

Suragch
fuente
1
EditText editText = customLayout.findViewById(R.id.editText); debería ser EditText editText = (EditText) customLayout.findViewById(R.id.editText);
philcruz
4
@philcruz, si actualizas a Android Studio 3.0, ya no tendrás que emitir explícitamente la vista. El IDE puede inferir el tipo. Es una característica muy bonita. Ayuda mucho a limpiar el código.
Suragch
gran respuesta, realmente útil
Noor Hossain
4

Las líneas de código más simples que me funcionan son las siguientes:

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(R.layout.layout_resource_id);
builder.show();

Cualquiera que sea el tipo de diseño (LinearLayout, FrameLayout, RelativeLayout) funcionará setView y solo diferirá en la apariencia y el comportamiento.

kenix
fuente
impresionante, simple y fácil
Frank Eno
2

La forma más fácil de hacer esto es usando en android.support.v7.app.AlertDialoglugar de android.app.AlertDialogwhere public AlertDialog.Builder setView (int layoutResId)se puede usar debajo de API 21.

new AlertDialog.Builder(getActivity())
    .setTitle(title)
    .setView(R.layout.dialog_basic)
    .setPositiveButton(android.R.string.ok,
        new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                //Do something
            }
        }
    )
    .setNegativeButton(android.R.string.cancel,
        new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                //Do something
            }
        }
    )
    .create();
flanaras
fuente
Cualquiera que lea esto después de la introducción de API 21, debería usar este método. Como menciona la respuesta, si su aplicación minSDKversion es menor que 21, use el AlerDialog del paquete de soporte. Voila !!!
Dibzmania
2

AlertDialog.setView(View view)agrega la vista dada al R.id.custom FrameLayout. El siguiente es un fragmento de código fuente de Android desde el AlertController.setupView()que finalmente maneja esto ( mViewes la vista dada al AlertDialog.setViewmétodo).

...
FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.**custom**);
custom.addView(**mView**, new LayoutParams(FILL_PARENT, FILL_PARENT));
...
Jung Soo Kim
fuente
1

Después de cambiar la ID en android.R.id.custom, necesitaba agregar lo siguiente para que se muestre la Vista:

((View) f1.getParent()).setVisibility(View.VISIBLE);

Sin embargo, esto hizo que la nueva Vista se renderizara en una vista principal grande sin fondo, rompiendo el cuadro de diálogo en dos partes (texto y botones, con la nueva Vista en el medio). Finalmente obtuve el efecto que quería insertando mi Vista junto al mensaje:

LinearLayout f1 = (LinearLayout)findViewById(android.R.id.message).getParent().getParent();

Encontré esta solución explorando el árbol de vistas con View.getParent () y View.getChildAt (int). Sin embargo, no estoy muy contento con ninguno de los dos. Nada de esto está en los documentos de Android y si alguna vez cambian la estructura de AlertDialog, esto podría romperse.

Mantha negro
fuente
1

Tendría más sentido hacerlo de esta manera, con la menor cantidad de código.

new AlertDialog.Builder(this).builder(this)
        .setTitle("Title")
        .setView(R.id.dialog_view)   //notice this setView was added
        .setCancelable(false)
        .setPositiveButton("Go", new DialogInterface.OnClickListener() {
            @Override 
            public void onClick(DialogInterface dialog, int id) {
                EditText textBox = (EditText) findViewById(R.id.textbox);
                doStuff();
            }
        }).show();

Para obtener una lista ampliada de las cosas que puede configurar, comience a escribir .seten Android Studio

StackAttack
fuente