Come implementare una visualizzazione AlertDialog personalizzata


107

Nei documenti Android su AlertDialog , fornisce le seguenti istruzioni ed esempio per impostare una visualizzazione personalizzata in un AlertDialog:

Se vuoi visualizzare una vista più complessa, cerca il FrameLayout chiamato "body" e aggiungici la tua vista:

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

Prima di tutto, è abbastanza ovvio che add()è un errore di battitura e dovrebbe esserlo addView().

Sono confuso dalla prima riga che usa R.id.body. Sembra che sia l'elemento del corpo di AlertDialog ... ma non posso semplicemente inserirlo nel mio codice b / c dà un errore di compilazione. Dove viene definito o assegnato R.id.body o altro?

Ecco il mio codice. Ho provato a usarlo setView(findViewById(R.layout.whatever)sul builder ma non ha funzionato. Suppongo perché non l'ho gonfiato 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();

Per trovare e utilizzare i tuoi oggetti in una finestra di dialogo, segui questi quattro passaggi: stackoverflow.com/a/18773261/1699586
Sara

3
Risposta in una riga: aggiungi .setView(getLayoutInflater().inflate(R.layout.dialog_view, null))al generatore. Ringraziamo Sergio Viudes, sotto.
1 ''

Risposte:


49

Hai ragione, è perché non l'hai gonfiato manualmente. Sembra che tu stia cercando di "estrarre" l'id "body" dal layout della tua attività e non funzionerà.

Probabilmente vuoi qualcosa del genere:

LayoutInflater inflater = getLayoutInflater();
FrameLayout f1 = (FrameLayout)alert.findViewById(android.R.id.body);
f1.addView(inflater.inflate(R.layout.dialog_view, f1, false));

17
È interessante notare che il corpo non è definito come una costante in android.R.id. Non mi è ancora chiaro come accedere all'elemento "body" del AlertDialog creato. Mi piacerebbe ancora sapere come farlo, ma per ora proverò a gonfiare una vista e utilizzare setView nel builder.
stormin986

2
In realtà questo mi lascia ancora con una domanda (sono nuovo nel gonfiare le visualizzazioni). Utilizzando builder.setView(inflater.inflate(R.id.dialog, ROOT_VIEWGROUP[, ATTACH_TO_ROOT])), i documenti dicono che il viewgroup root è facoltativo. Questo dovrebbe essere usato in questo caso? Se è così ... devo ancora capire come ottenere un riferimento al AlertDialog ...
stormin986

2
È opzionale, ma non avrai un riferimento al genitore dall'interno del layout che stai gonfiando. Cose come Android: layout_gravity non funzionerà nella vista di primo livello ... e forse non ne avrai bisogno. Quando chiami AlertDialog alert = builder.create (), hai un riferimento al tuo AlertDialog. Risposta lunga breve, è facoltativo. Provalo, a seconda di cosa stai facendo nel tuo layout personalizzato, probabilmente funzionerà.
synic

2
Non sono chiaro come fare riferimento alla vista all'interno di AlertDialog. Cosa consiglieresti di fare in questo caso se volessi fare riferimento al genitore? L'unica cosa che vedo all'interno di alertDialog che restituisce una visualizzazione è getCurrentFocus ()
stormin986

5
Tieniti stretto il Viewgonfiaggio. Chiama findViewById()il che Viewquando hai bisogno di roba dai suoi contenuti. Vedi: github.com/commonsguy/cw-android/tree/master/Database/Constants
CommonsWare

159

Puoi creare la tua vista direttamente dal Layout Inflater, devi solo usare il nome del tuo file XML di layout e l'ID del layout nel file.

Il tuo file XML dovrebbe avere un ID come questo:

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

E poi puoi impostare il tuo layout sul builder con il seguente codice:

LayoutInflater inflater = getLayoutInflater();
View dialoglayout = inflater.inflate(R.layout.dialog_layout, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(dialoglayout);
builder.show();

4
E dov'è R.id.dialog_layout_root in questo esempio? Non è una vista nell'attività corrente?
Alex Pretzlav

5
@AlexPretzlav: dialog_layout_root non è necessario in questo esempio. Tutto ciò di cui hai bisogno è il nome del tuo file xml per R.layout. [Name_of_xml_file].
IgorGanapolsky

7
@Temperage Hai aggiunto builder.show alla fine. Ho provato questo codice e ha funzionato. Inoltre ho passato null come secondo parametro per infalter.inflate
Vinoth

2
Questa dovrebbe essere stata selezionata come la migliore risposta.
Salman Khakwani

2
Ciò fornisce un'eccezione ClassCastException quando il layout personalizzato contiene un EditText, perché getCurrentFocus()restituirà l'EditText e non è possibile eseguire il cast di un EditText a un ViewGroup. L'uso nullcome secondo argomento risolve questo problema.
1 ''

20

android.R.id.custom restituiva null per me. Sono riuscito a farlo funzionare nel caso in cui qualcuno si imbattesse nello stesso 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();

Per riferimento, R.layout.simple_password è:

<?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. La tua soluzione funziona bene. Basta avere qualcosa dentro. Dobbiamo gonfiare prima di builder.create e impostare frameView.setLayoutParams (new LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); o qualcosa in primo luogo, che impedirà alcuni bug che non si aspettavano
MOBYTELAB

grazie per il feedback..come si gonfia prima di builder.create ()? inflate viene chiamato sul dispositivo di gonfiaggio della finestra di dialogo e ho solo una finestra di dialogo perché ho chiamato builder.create ()
John Rellis

possiamo usare activity inflater e non collegarci a FrameLayout, quindi possiamo ridurre un piccolo codice come this.AlertDialog.Builder builder = new AlertDialog.Builder (new ContextThemeWrapper (getActivity (), android.R.style.Theme_Holo)) .setTitle ("titolo") .setIcon (R.drawable.sample_icon); Visualizza troubleView = inflater.inflate (R.layout.sample_layout, null, false); builder.setView (troubleView); alert = builder.create (); Mi spiace, non so come scrivere chiaramente il codice nel commento
MOBYTELAB

Si tratta solo di bug di progettazione, se raccogliamo tutto in layout xml e dimentichiamo Framelayout come radice del dialogo, possiamo essere curiosi se il layout in xml non è fill genitore o qualcosa del genere, solo un caso.
MOBYTELAB

Ha funzionato per me, grazie! Questo mi consente di aggiungere un titolo e il mio pulsante Annulla e OK, incluso Modifica testo senza errori. :) L'unica domanda è: come funziona? Agisce FrameLayoutcome una sorta di frammento? Quando ho provato la risposta di Andrewx2 sopra, ho ricevuto un errore perché pensavo di gonfiare due layout (è la mia ipotesi).
Azurespot


12

Questo ha funzionato per me:

dialog.setView(dialog.getLayoutInflater().inflate(R.layout.custom_dialog_layout, null));

8

AlertDialog personalizzato

Questo esempio completo include il trasferimento dei dati all'attività.

inserisci qui la descrizione dell'immagine

Crea un layout personalizzato

Per EditTextquesto semplice esempio viene utilizzato un layout con una , ma puoi sostituirlo con qualsiasi cosa tu voglia.

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>

Usa la finestra di dialogo nel codice

Le parti fondamentali sono

  • utilizzando setViewper assegnare il layout personalizzato al fileAlertDialog.Builder
  • inviare i dati all'attività quando si fa clic su un pulsante di dialogo.

Questo è il codice completo del progetto di esempio mostrato nell'immagine sopra:

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

Appunti

  • Se ti accorgi di usarlo in più punti, considera la possibilità di creare una DialogFragmentsottoclasse come descritto nella documentazione .

Guarda anche


1
EditText editText = customLayout.findViewById(R.id.editText); dovrebbe essere EditText editText = (EditText) customLayout.findViewById(R.id.editText);
philcruz

4
@philcruz, se esegui l'aggiornamento ad Android Studio 3.0, non devi più trasmettere esplicitamente la vista. L'IDE può dedurre il tipo. È una caratteristica molto bella. Aiuta a ripulire molto il codice.
Suragch

ottima risposta, davvero utile
Noor Hossain

4

Le righe di codice più semplici che funzionano per me sono le seguenti:

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

Qualunque sia il tipo di layout (LinearLayout, FrameLayout, RelativeLayout) funzionerà setView e differirà solo nell'aspetto e nel comportamento.


fantastico, semplice e facile
Frank Eno

2

Il modo più semplice per farlo è usare android.support.v7.app.AlertDialoginvece di android.app.AlertDialogdove public AlertDialog.Builder setView (int layoutResId)può essere usato sotto l'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();

Chiunque legga questo dopo l'introduzione dell'API 21, dovrebbe usare questo metodo. Come menzionato nella risposta, se la versione minSDK della tua app è inferiore a 21, utilizza AlerDialog dal pacchetto di supporto. Ecco !!!
Dibzmania

2

AlertDialog.setView(View view)aggiunge la vista data al file R.id.custom FrameLayout. Quello che segue è uno snippet di codice sorgente Android da AlertController.setupView()cui finalmente gestisce questo ( mViewè la vista data al AlertDialog.setViewmetodo).

...
FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.**custom**);
custom.addView(**mView**, new LayoutParams(FILL_PARENT, FILL_PARENT));
...

1

Dopo aver modificato l'ID it android.R.id.custom, dovevo aggiungere quanto segue per visualizzare la vista:

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

Tuttavia, ciò ha causato il rendering della nuova vista in una grande vista padre senza sfondo, interrompendo la finestra di dialogo in due parti (testo e pulsanti, con la nuova vista in mezzo). Finalmente ho ottenuto l'effetto che volevo inserendo la mia Vista accanto al messaggio:

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

Ho trovato questa soluzione esplorando la struttura ad albero della vista con View.getParent () e View.getChildAt (int). Neanche per sogno, però. Niente di tutto questo è nei documenti di Android e se cambiano mai la struttura di AlertDialog, questo potrebbe interrompersi.


1

Avrebbe più senso farlo in questo modo, con una quantità minima di codice.

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

Per un elenco esteso di cose che puoi impostare, inizia a digitare .setin Android Studio

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.