So implementieren Sie eine benutzerdefinierte AlertDialog-Ansicht

106

In den Android-Dokumenten zu AlertDialog finden Sie die folgenden Anweisungen und Beispiele zum Festlegen einer benutzerdefinierten Ansicht in einem AlertDialog:

Wenn Sie eine komplexere Ansicht anzeigen möchten, rufen Sie das FrameLayout mit dem Namen "body" auf und fügen Sie Ihre Ansicht hinzu:

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

Zunächst einmal ist es ziemlich offensichtlich, dass dies add()ein Tippfehler ist und sein soll addView().

Ich bin durch die erste Zeile mit R.id.body verwirrt. Es scheint, dass es das Body-Element des AlertDialogs ist ... aber ich kann nicht einfach eingeben, dass es in meinem Code b / c einen Kompilierungsfehler gibt. Wo wird R.id.body definiert oder zugewiesen oder was auch immer?

Hier ist mein Code. Ich habe versucht, setView(findViewById(R.layout.whatever)den Builder zu verwenden, aber es hat nicht funktioniert. Ich nehme an, weil ich es nicht manuell aufgeblasen habe?

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
quelle
3
Einzeilige Antwort: .setView(getLayoutInflater().inflate(R.layout.dialog_view, null))Zum Builder hinzufügen . Dank an Sergio Viudes, unten.
1.

Antworten:

48

Sie haben Recht, weil Sie es nicht manuell aufgeblasen haben. Es scheint, dass Sie versuchen, die "body" -ID aus dem Layout Ihrer Aktivität zu "extrahieren", und das funktioniert nicht.

Sie möchten wahrscheinlich so etwas:

LayoutInflater inflater = getLayoutInflater();
FrameLayout f1 = (FrameLayout)alert.findViewById(android.R.id.body);
f1.addView(inflater.inflate(R.layout.dialog_view, f1, false));
synic
quelle
17
Interessanterweise ist der Körper in android.R.id nicht als Konstante definiert. Ich bin mir immer noch nicht sicher, wie ich auf das 'body'-Element des erstellten AlertDialogs zugreifen soll. Ich würde immer noch gerne wissen, wie das geht, aber jetzt werde ich nur versuchen, eine Ansicht aufzublasen und setView im Builder zu verwenden.
stormin986
2
Eigentlich habe ich dann noch eine Frage (ich bin neu darin, Ansichten aufzublähen). In builder.setView(inflater.inflate(R.id.dialog, ROOT_VIEWGROUP[, ATTACH_TO_ROOT]))den Dokumenten wird angegeben, dass die Stammansichtsgruppe optional ist. Sollte dies in diesem Fall verwendet werden? Wenn ja ... müssen Sie noch herausfinden, wie Sie einen Verweis auf den AlertDialog erhalten ...
stormin986
2
Es ist optional, aber dann haben Sie in dem aufgeblasenen Layout keinen Verweis auf das übergeordnete Element. Dinge wie android: layout_gravity funktionieren in der Toplevel-Ansicht nicht ... und vielleicht brauchen Sie sie auch nicht. Wenn Sie AlertDialog alert = builder.create () aufrufen, haben Sie einen Verweis auf Ihren AlertDialog. Lange Antwort kurz, es ist optional. Probieren Sie es aus, je nachdem, was Sie in Ihrem benutzerdefinierten Layout tun, wird es wahrscheinlich funktionieren.
Synchron
2
Mir ist nicht klar, wie ich auf die Ansicht im AlertDialog verweisen soll. Was würden Sie in diesem Fall empfehlen, wenn ich auf den Elternteil verweisen möchte? Das einzige, was ich in alertDialog sehe, das eine Ansicht zurückgibt, ist getCurrentFocus ()
stormin986
5
Halten Sie sich auf dem ViewSie aufblasen. Rufen Sie das findViewById()an, Viewwenn Sie Inhalte benötigen. Siehe: github.com/commonsguy/cw-android/tree/master/Database/Constants
CommonsWare
158

Sie können Ihre Ansicht direkt aus dem Layout Inflater erstellen. Sie müssen nur den Namen Ihrer Layout-XML-Datei und die ID des Layouts in der Datei verwenden.

Ihre XML-Datei sollte eine folgende ID haben:

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

Anschließend können Sie Ihr Layout im Builder mit dem folgenden Code festlegen:

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
quelle
4
Und wo ist R.id.dialog_layout_root in diesem Beispiel? Ist das nicht eine Ansicht in der aktuellen Aktivität?
Alex Pretzlav
5
@AlexPretzlav: dialog_layout_root wird in diesem Beispiel nicht benötigt. Sie benötigen lediglich den Namen Ihrer XML-Datei für R.layout. [Name_der_xml_Datei].
IgorGanapolsky
7
@Temperage Haben Sie am Ende builder.show hinzugefügt? Ich habe diesen Code ausprobiert und es hat funktioniert. Außerdem habe ich null als zweiten Parameter für infalter.inflate übergeben
Vinoth
2
Dies sollte als beste Antwort ausgewählt worden sein.
Salman Khakwani
2
Dies führt zu einer ClassCastException, wenn das benutzerdefinierte Layout einen EditText enthält, da getCurrentFocus()der EditText zurückgegeben wird und ein EditText nicht in eine ViewGroup umgewandelt werden kann. Die Verwendung nullals zweites Argument behebt dies.
1.
19

android.R.id.custom hat für mich null zurückgegeben. Ich habe es geschafft, dass dies funktioniert, falls jemand auf dasselbe Problem stößt.

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

Als Referenz lautet 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
quelle
John Rellis. Ihre Lösung funktioniert gut. Hab einfach was drin. Wir sollten vor builder.create aufblasen und frameView.setLayoutParams festlegen (neue LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); oder etwas zuerst, das einige Fehler verhindert, die nicht erwartet wurden
MOBYTELAB
Danke für das Feedback. Wie bläst du dich vor builder.create () auf? inflate wird auf dem inflater des Dialogs aufgerufen und ich habe nur einen Dialog, weil ich builder.create ()
John Rellis
Wir können den Aktivitäts-Inflater verwenden und nicht an FrameLayout anhängen, um einen kleinen Code wie diesen zu reduzieren. AlertDialog.Builder builder = new AlertDialog.Builder (neuer ContextThemeWrapper (getActivity (), android.R.style.Theme_Holo)) .setTitle ("title") .setIcon (R.drawable.sample_icon); View TroubleView = inflater.inflate (R.layout.sample_layout, null, false); builder.setView (TroubleView); alert = builder.create (); Entschuldigung, ich weiß nicht, wie ich Code klar auf Kommentar schreiben soll
MOBYTELAB
Es geht nur um Designfehler. Wenn wir alle in Layout-XML sammeln und Framelayout als Wurzel des Dialogs vergessen, können wir neugierig sein, ob Layout in XML nicht übergeordnet oder so ist, sondern nur ein Fall.
MOBYTELAB
Hat für mich gearbeitet, danke! Mit diesem kann ich einen Titel und meine Schaltfläche Abbrechen und OK hinzufügen, einschließlich des EditText ohne Fehler. :) Die einzige Frage ist, wie funktioniert das? Wirkt FrameLayoutals eine Art Fragment? Als ich die obige Antwort von Andrewx2 ausprobierte, bekam ich einen Fehler, weil ich dachte, ich würde zwei Layouts aufblasen (ist meine Vermutung).
Azurespot
12

Das hat bei mir funktioniert:

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

Benutzerdefinierter AlertDialog

Dieses vollständige Beispiel umfasst die Rückgabe von Daten an die Aktivität.

Geben Sie hier die Bildbeschreibung ein

Erstellen Sie ein benutzerdefiniertes Layout

EditTextFür dieses einfache Beispiel wird ein Layout mit einem verwendet, das Sie jedoch durch ein beliebiges ersetzen können.

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>

Verwenden Sie den Dialog im Code

Die wichtigsten Teile sind

  • Verwenden Sie setView, um das benutzerdefinierte Layout dem zuzuweisenAlertDialog.Builder
  • Senden von Daten an die Aktivität zurück, wenn auf eine Dialogschaltfläche geklickt wird.

Dies ist der vollständige Code aus dem im obigen Bild gezeigten Beispielprojekt:

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

Anmerkungen

  • Wenn Sie dies an mehreren Stellen verwenden, sollten Sie eine DialogFragmentUnterklasse erstellen, wie in der Dokumentation beschrieben .

Siehe auch

Suragch
quelle
1
EditText editText = customLayout.findViewById(R.id.editText); sollte sein EditText editText = (EditText) customLayout.findViewById(R.id.editText);
philcruz
4
@philcruz, Wenn Sie auf Android Studio 3.0 aktualisieren, müssen Sie die Ansicht nicht mehr explizit umwandeln. Die IDE kann auf den Typ schließen. Es ist eine sehr schöne Funktion. Es hilft viel, den Code zu bereinigen.
Suragch
tolle Antwort, sehr hilfreich
Noor Hossain
4

Die einfachsten Codezeilen, die für mich funktionieren, sind folgende:

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

Unabhängig von der Art des Layouts (LinearLayout, FrameLayout, RelativeLayout) funktioniert es setView und unterscheidet sich nur in Aussehen und Verhalten.

Kenix
quelle
genial, einfach und leicht
Frank Eno
2

Der einfachste Weg , dies zu tun ist , indem Sie android.support.v7.app.AlertDialogstatt , android.app.AlertDialogwo public AlertDialog.Builder setView (int layoutResId)kann unter API 21 verwendet werden.

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
quelle
Jeder, der dies nach Einführung von API 21 liest, sollte diese Methode verwenden. Wie in der Antwort erwähnt, verwenden Sie den AlerDialog aus dem Support-Paket, wenn Ihre App minSDKversion kleiner als 21 ist. Voila !!!
Dibzmania
2

AlertDialog.setView(View view)fügt die angegebene Ansicht zum hinzu R.id.custom FrameLayout. Das Folgende ist ein Ausschnitt aus dem Android-Quellcode, aus AlertController.setupView()dem dies schließlich behandelt wird ( mViewist die Ansicht, die der AlertDialog.setViewMethode gegeben wurde).

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

Nachdem ich die ID it android.R.id.custom geändert hatte, musste ich Folgendes hinzufügen, damit die Ansicht angezeigt wurde:

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

Dies führte jedoch dazu, dass die neue Ansicht in einer großen übergeordneten Ansicht ohne Hintergrund gerendert wurde, wodurch das Dialogfeld in zwei Teile aufgeteilt wurde (Text und Schaltflächen, dazwischen die neue Ansicht). Ich habe endlich den gewünschten Effekt erzielt, indem ich meine Ansicht neben die Nachricht eingefügt habe:

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

Ich habe diese Lösung gefunden, indem ich den Ansichtsbaum mit View.getParent () und View.getChildAt (int) untersucht habe. Aber auch nicht wirklich glücklich darüber. Nichts davon ist in den Android-Dokumenten enthalten. Wenn sie jemals die Struktur des AlertDialogs ändern, kann dies zu Problemen führen.

Schwarzer Mantha
quelle
1

Es wäre am sinnvollsten, dies auf diese Weise zu tun, am wenigsten Code.

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

Beginnen Sie mit der Eingabe .setin Android Studio , um eine erweiterte Liste der Einstellungen zu erhalten

StackAttack
quelle