Android界面开发--AlertDialog对话框的实现

参考资料

概述

对话框是提示用户做出决定或输入额外信息的小窗口。对话框不会填充屏幕,通常用于需要用户采取行动才能继续执行的模式事件。
在这里插入图片描述
Dialog 类是对话框的基类。但通常应该避免直接对其实例化,而是使用下列子类之一:

注意:Android 包含另一种名为ProgressDialog 的对话框类,该类可显示带有进度条的对话框。不推荐使用此微件,因为它会在显示进度的情况下阻止用户与应用交互。如果需要指示加载进度或不确定的进度,您应遵循进度和 Activity 的设计指南,并在布局中使用 ProgressBar,而非 ProgressDialog。

以上的类可以定义对话框的样式与结构,但需要应用DialogFragment作为对话框的容器。DialogFragment 类提供创建对话框和管理其外观所需的所有控件,而非调用 Dialog 对象上的方法。

使用 DialogFragment 管理对话框可确保它能正确处理生命周期事件,如用户按“返回”按钮或旋转屏幕时。此外,DialogFragment 类还允许以嵌入式组件的形式在较大界面中重复使用对话框的界面,类似于传统的 Fragment(例如,当希望让对话框界面在大屏幕和小屏幕上具有不同外观时)。

注意下文的对话框代码基于支持库的 android.support.v4.app.DialogFragment 类,而不是 android.app.DialogFragment。

创建对话框片段

通过扩展 DialogFragment 并在 onCreateDialog() 回调方法中创建 AlertDialog,可以完成各种对话框设计。
一个普通的对话框片段如下:

public class FireMissilesDialogFragment extends DialogFragment {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(R.string.dialog_fire_missiles)
               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // FIRE ZE MISSILES!
                   }
               })
               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // User cancelled the dialog
                   }
               });
        // Create the AlertDialog object and return it
        return builder.create();
    }
}

通过创建以上类的实例并调用对象上的show()方法,可以弹出对话框。
根据对话框的复杂度,可以在DialogFragment中实现各种其他回调方法,包括所有基本的片段生命周期方法。

构建提醒对话框

AlertDialog类允许您构建各种对话框设计,并且该类通常是需要的唯一对话框类。可以用AlertDialog.Builder类提供的API创建对话框。

添加列表

可通过AlertDialog API提供的三种列表:

  • 传统的单选列表
  • 永久性的单选列表(单选按钮)
  • 永久性的多选列表(复选框)

创建传统单选列表对话框

如果要创建传统单选列表,可以使用 setItems() 方法:

  @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder builder=new AlertDialog.Builder(getActivity());
        builder.setTitle("标题")
                .setItems(R.array.items_of_tranditional_single_select_dialog, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                        String a=String.valueOf(which);

                        String selectContent=getResources().getStringArray(R.array.items_of_tranditional_single_select_dialog)[which];
                        Toast.makeText(getContext(),"你选择了"+selectContent,Toast.LENGTH_SHORT).show();
                    }
                });

        return builder.create();
    }

在这里插入图片描述
由于列表出现在对话框的内容区域,因此对话框无法同时显示消息和列表,应通过 setTitle() 为对话框设置标题。如要为列表指定项目,可以调用 setItems(),并传递数组。或者可以使用 setAdapter() 指定列表。如此一来,便可使用ListAdapter ,以动态数据(如来自数据库的数据)支持列表。
如果选择通过ListAdapter 支持列表,务必使用 Loader ,以便内容以异步方式加载。 使用适配器构建布局 加载程序 指南中对此做了进一步描述。

注意:默认情况下,轻触列表项会关闭对话框,除非使用以下的某一种永久性选择列表

添加永久性多选列表和永久性单选列表

如要添加多选项(复选框)或单选项(单选按钮)列表,需要分别使用 setMultiChoiceItems() setSingleChoiceItems() 方法。

    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState)
    {
        selectItems=new ArrayList<>();
        AlertDialog.Builder builder=new AlertDialog.Builder(getActivity());
        builder.setTitle("标题")
                .setMultiChoiceItems(R.array.items_of_multi_select_dialog, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                        try {
                            if(isChecked)
                            {
                                selectItems.add(getResources().getStringArray(R.array.items_of_multi_select_dialog)[which]);
                            }
                            else
                            {
                                selectItems.remove(getResources().getStringArray(R.array.items_of_multi_select_dialog)[which]);
                            }
                        }
                        catch (Exception e)
                        {
                            e.printStackTrace();
                        }
                    }
                })
        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });

        return builder.create();
    }

在这里插入图片描述

尽管传统列表和带有单选按钮的列表均提供“单选”操作,但若想保留用户的选择,则应使用 setSingleChoiceItems()。换言之,若希望在稍后再次打开对话框时显示用户的当前选择,则需创建带有单选按钮的列表。

创建自定义布局

如果想让对话框拥有自定义布局,需要在视图中自定义布局内容,然后通过调动AlertDialog.Builder 对象上的 setView() ,将该布局添加至 AlertDialog。
默认情况下,自定义布局会填充对话框窗口,但仍可使用AlertDialog.Builder 方法来添加按钮和标题。
一个自定义布局如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center_horizontal"
    tools:context=".DialogTest.CustomLayoutDialogFragment">

    <!-- TODO: Update blank fragment layout -->

    <TextView
        android:id="@+id/textview_customDialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="@string/hello_blank_fragment"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button_customDialog" />

    <Button
        android:id="@+id/button_customDialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/checkBox_customDialog" />

    <ImageView
        android:id="@+id/imageView_customDialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@android:drawable/btn_star_big_on"
        tools:src="@android:drawable/btn_star_big_on" />

    <CheckBox
        android:id="@+id/checkBox_customDialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="CheckBox"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView_customDialog" />

</androidx.constraintlayout.widget.ConstraintLayout>

如果要扩展DialogFragment中的布局,需要通过 getLayoutInflater() 获取一个 LayoutInflater 并调用 inflate(),其中第一个参数是布局资源 ID,第二个参数是布局的父视图。然后,您可以调用 setView(),将布局放入对话框中。或者不需要使用LayoutInflater ,直接将layout资源ID传入setView()中也可以。

public class CustomLayoutDialogFragment extends DialogFragment {
    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder builder=new AlertDialog.Builder(getActivity());

        //LayoutInflater inflater=requireActivity().getLayoutInflater();
        builder.setView(R.layout.fragment_custom_layout_dialog)
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                    }
                });

        builder.setOnKeyListener(new DialogInterface.OnKeyListener() {
            @Override
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                return true;//屏蔽点击返回按钮取消对话框
            }
        });
        AlertDialog alertDialog=builder.create();
        alertDialog.setCanceledOnTouchOutside(false);//屏蔽对话框范围外的触屏取消
        return alertDialog;
    }
 }

在这里插入图片描述可以在自定义的对话框Fragment中获取视图组件对象并进行事件监听。

 @Override
    public void onResume() {
        super.onResume();
        Button btn=(Button)getDialog().findViewById(R.id.button_customDialog);

        CheckBox checkBox=(CheckBox)getDialog().findViewById(R.id.checkBox_customDialog);
        if (!btn.hasOnClickListeners())
        {
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                }
            });
        }
        if (!checkBox.hasOnClickListeners())
        {
            checkBox.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                }
            });

            checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                }
            });
        }
    }

将事件传递回对话框的宿主

如果需要将对话框的操作事件(如按钮点击,勾选等)或数据传递给弹出该对话框的宿主(Activity或Fragment)。需要定义接口。然后,从该对话框操作事件的宿主组件中实现这些接口。以上面自定义对话框为例。在CustomLayoutDialogFragment中定义接口,通过接口将事件传回宿主:

public class CustomLayoutDialogFragment extends DialogFragment {

    public interface CustomLayoutDialogListener
    {
        public void onDialogPositiveClick(DialogFragment dialog);
        public void onDialogNegativeClick(DialogFragment dialog);

    }
    CustomLayoutDialogListener customLayoutDialogListener;

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        try {
            customLayoutDialogListener=(CustomLayoutDialogListener) context;
        }
        catch (ClassCastException e)
        {            throw new ClassCastException(getActivity().toString()
                    + " must implement NoticeDialogListener");

        }
    }   
}

对话框的宿主 (AllKindsOfDialogActivity)会使用对话框片段的构造函数创建对话框实例,并通过 CustomLayoutDialogListener接口的实现接收接收对话框的事件:

public class AllKindsOfDialogActivity extends AppCompatActivity implements CustomLayoutDialogFragment.CustomLayoutDialogListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_all_kinds_of_dialog);
    }


    public void showCustomLayoutDialog(View view)
    {

        CustomLayoutDialogFragment customLayoutDialogFragment=new CustomLayoutDialogFragment();
        customLayoutDialogFragment.show(getSupportFragmentManager(),"customDialog");
    }
    
    @Override
    public void onDialogPositiveClick(DialogFragment dialog) {
        Toast.makeText(this,"Pos",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDialogNegativeClick(DialogFragment dialog) {
        Toast.makeText(this,"Neg",Toast.LENGTH_SHORT).show();
    }
}

由于宿主 Activity 会实现 CustomLayoutDialogListener (由以上显示的 onAttach() 回调方法强制执行),因此对话框片段可使用接口回调方法向 Activity 传递点击事件:

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder builder=new AlertDialog.Builder(getActivity());

        //LayoutInflater inflater=requireActivity().getLayoutInflater();
        builder.setView(R.layout.fragment_custom_layout_dialog)
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        customLayoutDialogListener.onDialogPositiveClick(CustomLayoutDialogFragment.this);
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        customLayoutDialogListener.onDialogNegativeClick(CustomLayoutDialogFragment.this);
                    }
                });

        builder.setOnKeyListener(new DialogInterface.OnKeyListener() {
            @Override
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                return true;
            }
        });
        AlertDialog alertDialog=builder.create();
        alertDialog.setCanceledOnTouchOutside(false);
        return alertDialog;
    }

全屏显示对话框或将其显示为嵌入式片段

不能使用 AlertDialog.Builder 或其他 Dialog 对象来构建对话框。如果想让 DialogFragment 拥有嵌入能力,则必须在布局中定义对话框的界面,然后在 onCreateView() 回调中加载布局。


public class FullScreenDialogFragment extends DialogFragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_full_screen_dialog, container, false);
    }

    /** The system calls this only when creating the layout in a dialog. */
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // The only reason you might override this method when using onCreateView() is
        // to modify any dialog characteristics. For example, the dialog includes a
        // title by default, but your custom layout might not need it. So here you can
        // remove the dialog title, but you must call the superclass to get the Dialog.
        Dialog dialog = super.onCreateDialog(savedInstanceState);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        return dialog;
    }
}

以下代码可根据具体需要确定将片段显示为对话框或全屏界面:

    public void showFullScreenDialog(View view)
    {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FullScreenDialogFragment fullScreenDialogFragment=new FullScreenDialogFragment();
        //fullScreenDialogFragment.show(fragmentManager, "fullScreenDialogFragment");弹出片段


        //以下为全屏显示
        //FragmentTransaction transaction = fragmentManager.beginTransaction();
        // For a little polish, specify a transition animation
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        // To make it fullscreen, use the 'content' root view as the container
        // for the fragment, which is always the root view for the activity
       // transaction.add(R.id.containerOfAllDialog, fullScreenDialogFragment)
                .addToBackStack(null).commit();//需要用id指定宿主的视图作为全屏显示片段的容器
    }

全屏显示中用到了片段事务,具体可以参考 执行片段事务

将 Activity 显示为大屏幕上的对话框

相对于在小屏幕上将对话框显示为全屏界面,也可以在大屏幕上将 Activity 显示为对话框,从而达到相同的效果。

如要仅在大屏幕上将 Activity 显示为对话框,请将 Theme.Holo.DialogWhenLarge 主题应用于 清单文件元素:

<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >

关闭对话框

当用户轻触使用 AlertDialog.Builder 创建的任何操作按钮时,系统会关闭对话框。

系统还会在用户轻触某个对话框列表项时关闭对话框,除非该列表使用单选按钮或复选框。否则,可以通过在 DialogFragment 上调用 dismiss() 来手动关闭对话框。

如需在对话框消失时执行特定操作,可以在 DialogFragment 中实现 onDismiss() 方法。

还可取消对话框。此特殊事件表示用户显式离开对话框,且并未完成任务。如果用户按“返回”按钮、轻触对话框区域外的屏幕,或者在 Dialog 上显式调用 cancel()(例如,为响应对话框中的“取消”按钮),就会发生这种情况。

如上例所示,可以通过在 DialogFragment 类中实现 onCancel() 来响应取消事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值