如何实现自定义AlertDialog视图


107

AlertDialog上Android文档中,它提供了以下说明和示例,用于在AlertDialog中设置自定义视图:

如果要显示更复杂的视图,请查找称为“ body”的FrameLayout并将视图添加到其中:

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

首先,很明显这add()是一个错字,并且本来就是addView()

我对使用R.id.body的第一行感到困惑。看来这是AlertDialog的主体元素,但我不能只在代码b / c中输入它,它会产生编译错误。R.id.body在哪里定义或分配的?

这是我的代码。我试图setView(findViewById(R.layout.whatever)在生成器上使用,但是没有用。我以为是因为我没有手动充气?

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

要在对话框上查找和使用对象,请执行以下四个步骤: stackoverflow.com/a/18773261/1699586
Sara

3
单行答案:添加.setView(getLayoutInflater().inflate(R.layout.dialog_view, null))到构建器。感谢下面的Sergio Viudes。
1英寸

Answers:


49

您是正确的,这是因为您没有手动对其进行充气。看来您正在尝试从“活动”的布局中“提取”“ body” ID,但这是行不通的。

您可能想要这样的东西:

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

17
有趣的是,在android.R.id中没有将body定义为常量。我仍然不清楚如何访问创建的AlertDialog的'body'元素。我仍然想知道如何执行此操作,但是现在,我将仅尝试为视图充气并在构建器中使用setView。
stormin986

2
实际上,这仍然让我有一个问题(我是膨胀视图的新手)。使用builder.setView(inflater.inflate(R.id.dialog, ROOT_VIEWGROUP[, ATTACH_TO_ROOT])),文档说根视图组是可选的。在这种情况下应该使用吗?如果是这样的话...仍然必须弄清楚如何获得对AlertDialog的引用...
stormin986 2010年

2
它是可选的,但是这样就不会在要放大的布局内部引用父对象。诸如android:layout_gravity之类的内容无法在顶级视图上运行...也许您不需要它们。调用AlertDialog alert = builder.create()时,将引用AlertDialog。长答案简短,它可选的。试试看,这取决于您在自定义布局中的工作,它可能会起作用。
synic 2010年

2
我不清楚如何在AlertDialog中引用视图。如果我确实想引用父母,在这种情况下,您会建议做什么?我在alertDialog中看到的唯一返回视图的东西是getCurrentFocus()
stormin986

5
保持View膨胀。调用findViewById()View,当你需要从它的内容的东西。参见:github.com/commonsguy/cw-android/tree/master/Database/Constants
CommonsWare,

159

您可以直接从Layout Inflater创建视图,只需要使用布局XML文件的名称和文件中布局的ID。

您的XML文件应具有以下ID:

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

然后,您可以使用以下代码在构建器上设置布局:

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
在此示例中,R.id.dialog_layout_root在哪里?这不是当前活动中的视图吗?
Alex Pretzlav 2011年

5
@AlexPretzlav:在此示例中不需要dialog_layout_root。您只需要R.layout。[name_of_xml_file]的xml文件名即可。
IgorGanapolsky

7
@Temperage是否在最后添加了builder.show。我尝试了这段代码,它起作用了。我也通过null作为infalter.inflate的第二个参数
Vinoth

2
这应该已经被选为最佳答案。
Salman Khakwani 2013年

2
当自定义布局包含EditText时,getCurrentFocus()将给出ClassCastException,因为它将返回EditText,并且EditText不能强制转换为ViewGroup。使用null作为第二个参数修复此。
1

20

android.R.id.custom为我返回null。如果有人遇到相同的问题,我设法使它起作用

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

供参考,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>

约翰·里利斯 您的解决方案工作正常。只是里面有东西。我们应该在builder.create和set frameView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT))之前充气。或首先解决某些无法预期的错误
MOBYTELAB

感谢您的反馈。.您如何在builder.create()之前充气?在对话框的充气机上调用了inflate,并且只有一个对话框,因为我已经调用了builder.create()
John Rellis 2013年

我们可以使用活动充气器,而不是附加到FrameLayout,因此可以减少这样的代码。 (“标题”).setIcon(R.drawable.sample_icon); 查看故障视图= inflater.inflate(R.layout.sample_layout,null,false); builder.setView(troubleView); 警报= builder.create(); 抱歉,我不知道如何在注释中清楚地编写代码
MOBYTELAB 2013年

这只是一个设计错误,如果我们将所有内容都收集在布局xml中,而忘记了Framelayout作为对话框的根,那么我们会好奇xml中的布局是不是填充父对象或某物,只是一个案例。
MOBYTELAB

为我工作,谢谢!这使我可以添加标题以及“取消”和“确定”按钮,包括没有错误的EditText。:)唯一的问题是,这如何工作?是否FrameLayout充当各种碎片?当我尝试上述Andrewx2的答案时,出现一个错误,因为它认为我在夸大两种布局(这是我的猜测)。
Azurespot 2014年



8

自定义AlertDialog

这个完整的示例包括将数据传递回活动。

在此处输入图片说明

创建自定义布局

EditText这个简单的示例使用带有的布局,但是您可以用任何喜欢的布局替换它。

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>

在代码中使用对话框

关键部分是

  • 使用setView指定的自定义布局到AlertDialog.Builder
  • 单击对话框按钮后,会将所有数据发送回活动。

这是上图所示示例项目的完整代码:

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

笔记

  • 如果您发现自己在多个地方都使用过此功能,请考虑DialogFragment按照文档中的说明制作一个子类。

也可以看看


1
EditText editText = customLayout.findViewById(R.id.editText); 应该是 EditText editText = (EditText) customLayout.findViewById(R.id.editText);
philcruz

4
@philcruz,如果您升级到Android Studio 3.0,则不再需要显式投射视图。IDE可以推断类型。这是一个非常好的功能。它有助于大量清理代码。
Suragch

很好的答案,真的很有帮助
Noor Hossain

4

适用于我的最简单的代码行如下:

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

无论哪种布局类型(LinearLayout,FrameLayout,RelativeLayout)均可使用,setView 并且外观和行为都不同。


很棒,简单又轻松
Frank Eno

2

最简单的方法是使用,android.support.v7.app.AlertDialog而不是在API 21 android.app.AlertDialogpublic AlertDialog.Builder setView (int layoutResId)可以在何处使用。

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

引入API 21之后阅读此书的任何人都应使用此方法。答案提到,如果您的应用程序minSDKversion小于21,请使用支持包中的AlerDialog。瞧!
Dibzmania

2

AlertDialog.setView(View view)确实将给定的视图添加到中R.id.custom FrameLayout。以下是Android源代码的一段AlertController.setupView(),最终从中处理了这一点(mViewAlertDialog.setView方法的视图)。

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

1

在将ID更改为android.R.id.custom之后,我需要添加以下内容以显示视图:

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

但是,这导致新视图在没有背景的大父视图中呈现,从而将对话框分为两部分(文本和按钮,中间是新视图)。通过在消息旁边插入“视图”,终于得到了想要的效果:

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

我通过使用View.getParent()和View.getChildAt(int)探索“视图”树找到了该解决方案。不过,也不是很高兴。这些都不在Android文档中,如果他们更改了AlertDialog的结构,则可能会中断。


1

以这种方式执行此操作最有意义,代码量最少。

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

有关您可以设置的扩展列表,请开始.set在Android Studio中输入

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.