如何实现自定义的 AlertDialog 视图

Posted

技术标签:

【中文标题】如何实现自定义的 AlertDialog 视图【英文标题】:How to implement a custom AlertDialog View 【发布时间】:2011-02-17 05:27:18 【问题描述】:

android docs on AlertDialog 中,它提供了以下说明和示例,用于在 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();

【问题讨论】:

要在对话框中查找和使用您的对象,请按照以下四个步骤操作:***.com/a/18773261/1699586 一站式回答:将.setView(getLayoutInflater().inflate(R.layout.dialog_view, null))添加到构建器。归功于下面的 Sergio Viudes。 【参考方案1】:

您可以直接从 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_
              android:layout_
              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();

【讨论】:

在这个例子中,R.id.dialog_layout_root 在哪里?这不是当前Activity中的视图吗? @AlexPretzlav:此示例中不需要 dialog_layout_root。您所需要的只是 R.layout.[name_of_xml_file] 的 xml 文件的名称。 @Temperage 你最后加了builder.show吗。我尝试了这段代码,它奏效了。我也将 null 作为 infalter.inflate 的第二个参数传递 这应该被选为最佳答案。 当自定义布局包含 EditText 时,这会产生 ClassCastException,因为 getCurrentFocus() 将返回 EditText 并且 EditText 无法转换为 ViewGroup。使用null 作为第二个参数可以解决这个问题。【参考方案2】:

你是对的,这是因为你没有手动充气。您似乎正在尝试从 Activity 的布局中“提取”“body”id,但这是行不通的。

你可能想要这样的东西:

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

【讨论】:

有趣的是,body 没有在 android.R.id 中定义为常量。我仍然不清楚如何访问创建的 AlertDialog 的“body”元素。我仍然想知道如何做到这一点,但现在我将尝试扩充视图并在构建器中使用 setView。 实际上这仍然给我留下了一个问题(我是膨胀视图的新手)。使用builder.setView(inflater.inflate(R.id.dialog, ROOT_VIEWGROUP[, ATTACH_TO_ROOT])),文档说根视图组是可选的。在这种情况下应该使用它吗?如果是这样......仍然必须弄清楚如何获得对 AlertDialog 的引用...... 它是可选的,但是你不会从你正在膨胀的布局内部获得对父级的引用。像 android:layout_gravity 这样的东西在顶层视图上不起作用......也许你不需要它们。当您调用 AlertDialog alert = builder.create() 时,您将获得对 AlertDialog 的引用。长答案简短,它可选的。试一试,这取决于您在自定义布局中所做的工作,它可能会起作用。 我不清楚如何在 AlertDialog 中引用 view。如果我确实想引用父母,你会建议在这种情况下做什么?我在 alertDialog 中看到的唯一返回视图的是 getCurrentFocus() 握住View你膨胀了。当您需要其内容中的内容时,请在 View 上致电 findViewById()。见:github.com/commonsguy/cw-android/tree/master/Database/Constants【参考方案3】:

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_
          android:layout_>

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

</LinearLayout>

【讨论】:

约翰·瑞利斯。您的解决方案工作正常。只要有东西在里面。我们应该在 builder.create 之前充气并设置 frameView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));或首先,这将防止一些意外的错误 感谢您的反馈。您如何在 builder.create() 之前进行充气?在对话框的充气器上调用了 inflate,我只有一个对话框,因为我调用了 builder.create() 我们可以使用activity inflater,而不是附加到FrameLayout,所以我们可以像这样减少一些代码。AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(getActivity(), android.R .style.Theme_Holo)) .setTitle("title") .setIcon(R.drawable.sample_icon);查看 TroubleView = inflater.inflate(R.layout.sample_layout, null, false); builder.setView(troubleView);警报 = builder.create();对不起,我不知道如何在评论中清楚地编写代码 这只是一个设计错误,如果我们在布局xml中收集所有内容并忘记Framelayout作为对话框的根,我们可能会好奇xml中的布局是否没有填充父或什么,只是一个案例。 为我工作,谢谢!这使我能够添加标题和我的取消和确定按钮,包括没有错误的 EditText。 :) 唯一的问题是,这是如何工作的? FrameLayout 是否充当某种片段?当我在上面尝试 Andrewx2 的答案时,我得到了一个错误,因为它认为我在夸大两个布局(是我的猜测)。【参考方案4】:

android 文档已被编辑以纠正错误。

AlertDialog 内的视图称为android.R.id.custom

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

【讨论】:

【参考方案5】:

自定义警报对话框

此完整示例包括将数据传回 Activity。

创建自定义布局

这个简单的例子使用了带有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_
              android:layout_>

    <EditText
        android:id="@+id/editText"
        android:layout_
        android:layout_/>

</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 子类,如documentation 中所述。

另见

Android Alert Dialog with one, two, and three buttons How can I display a list view in an Android Alert Dialog?

【讨论】:

EditText editText = customLayout.findViewById(R.id.editText); 应该是EditText editText = (EditText) customLayout.findViewById(R.id.editText); @philcruz,如果升级到 Android Studio 3.0,则不再需要显式转换视图。 IDE 可以推断类型。这是一个非常好的功能。对清理代码很有帮助。 很好的答案,真的很有帮助【参考方案6】:

这对我有用:

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

【讨论】:

【参考方案7】:

对我有用的最简单的代码行如下:

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

无论哪种类型的布局(LinearLayout、FrameLayout、RelativeLayout)都适用于setView 并且只会在外观和行为上有所不同。

【讨论】:

棒极了,简单易行【参考方案8】:

最简单的方法是使用android.support.v7.app.AlertDialog 而不是android.app.AlertDialog,其中public AlertDialog.Builder setView (int layoutResId) 可以在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();

【讨论】:

在 API 21 引入后阅读此内容的任何人都应使用此方法。如答案所述,如果您的应用 minSDKversion 小于 21,请使用支持包中的 AlerDialog。瞧!!!【参考方案9】:

AlertDialog.setView(View view) 确实将给定视图添加到R.id.custom FrameLayout。以下是来自AlertController.setupView() 的Android 源代码的sn-p,它最终处理了这个问题(mView 是给AlertDialog.setView 方法的视图)。

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

【讨论】:

【参考方案10】:

在将 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 的结构,这可能会中断。

【讨论】:

【参考方案11】:

这样做最有意义,代码量最少。

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

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

【讨论】:

以上是关于如何实现自定义的 AlertDialog 视图的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Xamarin Android 的 AlertDialog 中创建带有自定义适配器的列表视图

如何在 ListView 和 AlertDialog 中设置自定义字体?

AlertDialog:如何删除视图上方和下方的黑色边框

使用自定义视图的AlertDialog

在 DialogFragment 中为 AlertDialog 膨胀自定义视图时出现问题

markdown 自定义视图的alertDialog