114

In the Android docs on AlertDialog, it gives the following instruction and example for setting a custom view in an AlertDialog:

If you want to display a more complex view, look up the FrameLayout called "body" and add your view to it:
FrameLayout fl = (FrameLayout) findViewById(R.id.body);
fl.add(myView, new LayoutParams(FILL_PARENT, WRAP_CONTENT));

First off, it's pretty obvious that add() is a typo and is meant to be addView().

I'm confused by the first line using R.id.body. It seems that it's the body element of the AlertDialog ... but I can't just enter that in my code b/c it gives a compile error. Where does R.id.body get defined or assigned or whatever?

Here's my code. I tried to use setView(findViewById(R.layout.whatever) on the builder but it didn't work. I'm assuming because I didn't manually inflate it?

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();
computingfreak
  • 4,939
  • 1
  • 34
  • 51
stormin986
  • 7,672
  • 15
  • 42
  • 54
  • To find and use your objects on a Dialog, follow this four step: http://stackoverflow.com/a/18773261/1699586 – Sara Sep 12 '13 at 20:15
  • 6
    One-line answer: add `.setView(getLayoutInflater().inflate(R.layout.dialog_view, null))` to the builder. Credit to Sergio Viudes, below. – 1'' Jan 19 '14 at 05:17

12 Answers12

160

You can create your view directly from the Layout Inflater, you only need to use the name of your layout XML file and the ID of the layout in file.

Your XML file should have an ID like this:

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

And then you can set your layout on the builder with the following code:

LayoutInflater inflater = getLayoutInflater();
View dialoglayout = inflater.inflate(R.layout.dialog_layout, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(dialoglayout);
builder.show();
Iman Marashi
  • 5,593
  • 38
  • 51
Andrewx2
  • 3,381
  • 4
  • 16
  • 5
  • 4
    And where is R.id.dialog_layout_root in this example? Isn't that a view in the current Activity? – Alex Pretzlav Dec 28 '11 at 01:59
  • 5
    @AlexPretzlav: dialog_layout_root is not needed in this example. All you need is the name of your xml file for R.layout.[name_of_xml_file]. – IgorGanapolsky Mar 28 '12 at 23:01
  • I have just tested the code above, and it does not work. Not sure why people continue to upvote it. – Temperage May 15 '12 at 16:33
  • 1
    contrariwise, it works while android.R.id.custom still returns null – lorenzoff Jun 09 '12 at 13:31
  • 7
    @Temperage Did you add builder.show at the end. I tried this code and it worked. Also i passed null as second parameter for infalter.inflate – Vinoth Jun 18 '12 at 07:52
  • 2
    This should have been selected as the Best Answer. – Salman Khakwani Dec 31 '13 at 09:31
  • 2
    This gives a ClassCastException when the custom layout contains an EditText, because `getCurrentFocus()` will return the EditText and an EditText can't be cast to a ViewGroup. Using `null` as the second argument fixes this. – 1'' Jan 19 '14 at 05:16
  • how to close the alert dialog now? – Mr.Ghamkhar Dec 18 '14 at 23:39
  • I get an error when I put this code inside of a button `onClick` method, and I think it's because I am adding other things to my `Dialog Box` like a title and 2 buttons. Is there another way around this? The error is basically because it thinks 2 `Views` are trying to inflate (one is the `EditText`, the other for the other things mentioned), but not sure how to get it all into one `View`. Any suggestions welcomed, thanks. – Azurespot Dec 30 '14 at 02:45
  • `View v = inflater.inflate(R.layout.activity_main, null);` generates a warning. Using `View.inflate(..., null, false)` just hides the warning but doesn't solve the problem. The correct method is to use `builder.setView(R.layout.edit_account_dialog); dialog = builder.create();` but `findViewById` will be usable only after the activity has called `dialog.show()` ; it rises another problem: the dialog's `findViewById` aren't useable before `show()` and aren't callable easily after `show()`. Btw calling the `findViewById` in the activity just after `dialog.show()` isn't a good practice. – JarsOfJam-Scheduler Mar 15 '19 at 22:16
51

You are correct, it's because you didn't manually inflate it. It appears that you're trying to "extract" the "body" id from your Activity's layout, and that won't work.

You probably want something like this:

LayoutInflater inflater = getLayoutInflater();
FrameLayout f1 = (FrameLayout)alert.findViewById(android.R.id.body);
f1.addView(inflater.inflate(R.layout.dialog_view, f1, false));
synic
  • 26,359
  • 20
  • 111
  • 149
  • 18
    Interestingly enough, body is not defined as a constant in android.R.id. I'm still not clear on how to access the 'body' element of the created AlertDialog. I'd still like to know how to do this, but for now I will just try to inflate a view and use setView in the builder. – stormin986 May 08 '10 at 19:36
  • 2
    Actually this still leave me with a question then (I'm new to inflating views). Using `builder.setView(inflater.inflate(R.id.dialog, ROOT_VIEWGROUP[, ATTACH_TO_ROOT]))`, the docs say the root viewgroup is optional. Should this be used in this case? If so ... still have to figure out how to get a reference to the AlertDialog... – stormin986 May 08 '10 at 19:44
  • 2
    It is optional, but then you won't have a reference to the parent from inside the layout you're inflating. Things like android:layout_gravity won't work on the toplevel view... and maybe you don't need them to. When you call AlertDialog alert = builder.create(), you have a reference to your AlertDialog. Long answer short, it *is* optional. Give it a try, depending on what you're doing in your custom layout, it'll probably work. – synic May 08 '10 at 19:50
  • 2
    I'm not clear on how to reference the *view* within the AlertDialog. What would you recommend doing in this case if I did want to reference the parent? The only thing I see within alertDialog that returns a view is getCurrentFocus() – stormin986 May 08 '10 at 19:57
  • 1
    Interesting. I got it to display just fine the view using `null` for the root view. My custom layout has an EditText element in it. The following returns `null`, however! `EditText textBox = (EditText) findViewById(R.id.chat_username);` Is there a reason the Activity findViewById didn't work here? I wasn't aware of any nuances in this method, I assumed it was universal. I'm not sure how to call alert.findViewById when I'm still setting up the builder. – stormin986 May 08 '10 at 20:10
  • 5
    Hold onto the `View` you inflate. Call `findViewById()` on that `View` when you need stuff from its contents. See: http://github.com/commonsguy/cw-android/tree/master/Database/Constants/ – CommonsWare May 08 '10 at 20:19
  • Great. Thanks! Worked like a charm. One odd thing happening: I inflate the view at the top level of my above code, and when I try to reference it in the anonymous inner class used for onClick, I get this error: "Cannot refer to a non-final variable v inside an inner class defined in a different method". Making my inflated view object final fixes it, but why is this necessary? – stormin986 May 08 '10 at 20:33
  • 1
    Here is the answer for your inner/final question: http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#228205 "Any local variable, formal method parameter or exception handler parameter used but not declared in an inner class must be declared final, and must be definitely assigned (§16) before the body of the inner class" – berlindev Jul 19 '11 at 13:13
  • it should actually be `android.R.id.custom` instead of `android.R.id.body` – Bobby Sep 04 '15 at 15:40
  • Customizing alert dialog makes your code dirty and unreadable. See http://learnzone.info/android-tutorial-custom-alert-dialog-using-dialogfragment/ for simple step by step guide to customize Alert Dialog. – Madhav Bhattarai Mar 29 '17 at 07:27
  • `View v = inflater.inflate(R.layout.activity_main, null);` generates a warning. Using `View.inflate(..., null, false)` just hides the warning but doesn't solve the problem. The correct method is to use `builder.setView(R.layout.edit_account_dialog); dialog = builder.create();` but `findViewById` will be usable only after the activity has called `dialog.show()` ; it rises another problem: the dialog's `findViewById` aren't useable before `show()` and aren't callable easily after `show()`. Btw calling the `findViewById` in the activity just after `dialog.show()` isn't a good practice. – JarsOfJam-Scheduler Mar 15 '19 at 22:16
21

android.R.id.custom was returning null for me. I managed to get this to work in case anybody comes across the same issue,

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

For reference, R.layout.simple_password is :

<?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
  • 543
  • 3
  • 15
  • John Rellis. Your solution works fine. Just have something in it. We should inflate before builder.create and set frameView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); or something first, that will prevent some bugs that not expected – MOBYTELAB Jul 31 '13 at 11:09
  • thanks for the feedback..how do you inflate before builder.create()? inflate is called on the dialog's inflater and I only have a dialog because I have called builder.create() – John Rellis Jul 31 '13 at 12:32
  • we can use activity inflater, and not attach to FrameLayout, so we can reduce a little code like this.AlertDialog.Builder builder = new AlertDialog.Builder(new 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(); Sorry, i don't know how to write code clearly on comment – MOBYTELAB Jul 31 '13 at 14:00
  • It's just about design bug, if we gather all in layout xml and forget Framelayout as a root of dialog, we can be curious if layout in xml is not fill parent or something, just a case. – MOBYTELAB Jul 31 '13 at 14:09
  • Worked for me, thanks! This one enables me to add a title and my Cancel and Okay button, including the EditText without error. :) Only question is, how does this work? Does `FrameLayout` act as a fragment of sorts? When I tried Andrewx2's answer above, I got an error because it thought I was inflating two layouts (is my guess). – Azurespot Dec 30 '14 at 02:58
17

The android documents have been edited to correct the errors.

The view inside the AlertDialog is called android.R.id.custom

http://developer.android.com/reference/android/app/AlertDialog.html

Ziem
  • 6,579
  • 8
  • 53
  • 86
M Dapp
  • 1,453
  • 16
  • 31
15

Custom AlertDialog

This full example includes passing data back to the Activity.

enter image description here

Create a custom layout

A layout with an EditText is used for this simple example, but you can replace it with anything you like.

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>

Use the dialog in code

The key parts are

  • using setView to assign the custom layout to the AlertDialog.Builder
  • sending any data back to the activity when a dialog button is clicked.

This is the full code from the example project shown in the image above:

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

Notes

  • If you find yourself using this in multiple places, then consider making a DialogFragment subclass as is described in the documentation.

See also

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • 1
    `EditText editText = customLayout.findViewById(R.id.editText);` should be `EditText editText = (EditText) customLayout.findViewById(R.id.editText);` – philcruz Nov 17 '17 at 23:36
  • 5
    @philcruz, If you upgrade to Android Studio 3.0, you no longer have to explicitly cast the view. The IDE can infer the type. It is a very nice feature. It helps to clean up the code a lot. – Suragch Nov 18 '17 at 08:22
  • great answer, really helpful – Noor Hossain Jul 05 '20 at 05:36
12

This worked for me:

dialog.setView(dialog.getLayoutInflater().inflate(R.layout.custom_dialog_layout, null));
Sergio Viudes
  • 2,714
  • 5
  • 26
  • 44
4

The simplest lines of code that works for me are are follows:

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

Whatever the type of layout(LinearLayout, FrameLayout, RelativeLayout) will work by setView and will just differ in the appearance and behavior.

ρяσѕρєя K
  • 132,198
  • 53
  • 198
  • 213
kenix
  • 124
  • 6
3

The easiest way to do this is by using android.support.v7.app.AlertDialog instead of android.app.AlertDialog where public AlertDialog.Builder setView (int layoutResId) can be used below 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
  • 92
  • 8
  • Anybody reading this after API 21 was introduced, should use this method. As the answer mentions, if your app minSDKversion is less than 21, use the AlerDialog from support package. Voila !!! – Dibzmania Feb 24 '17 at 00:54
2

AlertDialog.setView(View view) does add the given view to the R.id.custom FrameLayout. The following is a snippet of Android source code from AlertController.setupView() which finally handles this (mView is the view given to AlertDialog.setView method).

...
FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.**custom**);
custom.addView(**mView**, new LayoutParams(FILL_PARENT, FILL_PARENT));
...
double-beep
  • 5,031
  • 17
  • 33
  • 41
1

After changing the ID it android.R.id.custom, I needed to add the following to get the View to display:

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

However, this caused the new View to render in a big parent view with no background, breaking the dialog box in two parts (text and buttons, with the new View in between). I finally got the effect that I wanted by inserting my View next to the message:

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

I found this solution by exploring the View tree with View.getParent() and View.getChildAt(int). Not really happy about either, though. None of this is in the Android docs and if they ever change the structure of the AlertDialog, this might break.

Black Mantha
  • 1,147
  • 8
  • 11
1

It would make the most sense to do it this way, least amount of 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();

For an expanded list of things you can set, start typing .set in Android Studio

StackAttack
  • 1,139
  • 1
  • 9
  • 15
0

This works for me ! and don't forget to implement the listener.

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.add_employee, null);
Tyler2P
  • 2,324
  • 26
  • 22
  • 31