• Dialog 继承重写 Dialog 实现一个自定义的Dialog
  • AlertDialog Android原生提供的对话框(底层是继承 Dialog 实现)
  • PopupWindow 用弹出悬浮框,实现对话框。这种对话框可以用在指定位置显示,一般用于一些非常小的按键弹窗。怎么实现可以参考我的博客: https://www.cnblogs.com/guanxinjing/p/10156153.html
  • 这3种弹窗对话框都有一个问题,就是与activity的生命周期不是捆绑的,得时刻注意在activity后台之后关闭Dialog。所以,后面google推荐使用DialogFragment来取代它们。 DialogFragment 本质其实是 Fragment ,有 Fragment 的生命周期并且与创建它的activity有捆绑,在google推出了Jetpack系列后,配合 Jetpack系列 LiveData 与navigation在使用上比一般的Dialog安全更多,并且在数据传递上也非常简单,配合 navigation 架构管理起来也十分简单明晰。

    如果你未接触过不了解 Jetpack系列,可以参考我的博客: https://www.cnblogs.com/guanxinjing/category/1550385.html 了解完 Jetpack系列 ,你就可以明白google为什么推出这种对话框了。

    下面我们就根据2个最简单demo和与一些使用特例,来介绍 DialogFragment的使用。

    Dialog 创建 DialogFragment的简单Demo

    DialogFragment 有2种方法创建我们需要的对话框内容,其中就有以Dialog来创建内容方式。

    继承重写 DialogFragment:

    public class MyDialog1 extends DialogFragment {
        @NonNull
        @Override
        public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
            //创建对话框,我们需要返回dialog
            AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
            dialog.setTitle("测试Dialog");
            dialog.setMessage("DialogFragment");
            return dialog.create();
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            //此方法在视图已经创建后返回的,但是这个view 还没有添加到父级中,我们在这里可以重新设定view的各个数据
    

    在activity里显示对话框:

            mBtnTest.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    myDialog = new MyDialog1();
                    myDialog.show(getSupportFragmentManager(),"myDialog");
    

    布局View创建DialogFragment的简单Demo

      DialogFragment另一种创建内容方法,导入一个View

    public class MyDialog extends DialogFragment {
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false);
            return view;
        @Override
        public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            此方法在视图View已经创建后返回的,但是这个view 还没有添加到父级中。
            我们在这里可以重新设定view的各个数据,但是不能修改对话框最外层的ViewGroup的布局参数。
            因为这里的view还没添加到父级中,我们需要在下面onStart生命周期里修改对话框尺寸参数
        @Override
        public void onStart() {
                因为View在添加后,对话框最外层的ViewGroup并不知道我们导入的View所需要的的宽度。 所以我们需要在onStart生命周期里修改对话框尺寸参数
            WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
            params.width = ViewGroup.LayoutParams.MATCH_PARENT;
            getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params);
            super.onStart();
    

    显示对话框的代码跟上面的demo一样,就不重复贴出来了,看看效果图:

    改变对话框的显示位置

    public class MyDialog2 extends DialogFragment {
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false);
            return view;
        @Override
        public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
        @Override
        public void onStart() {
            WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
            params.width = ViewGroup.LayoutParams.MATCH_PARENT;
            params.gravity = Gravity.BOTTOM; //将对话框放到布局下面,也就是屏幕下方
            getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params);
            super.onStart();
    

    将对话框的宽或者高铺满屏幕

    设置对话框铺满屏幕有2种方式:

    第一种  需要在styles.xml文件里,添加一个没有内边距的style,如下

      在上面的出现在屏幕下方的对话框中,依然与屏幕有小段距离,那个其实是dialog自带的padding内边距属性导致的。这种方式可以设置只在宽度上铺满屏幕,但是高度上依然留有一定的内边距。

        <style name="dialogFullScreen" parent="Theme.AppCompat.Dialog">
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="android:padding">0dp</item>
            <item name="android:windowBackground">@android:color/white</item>
            <item name="android:textColor">@android:color/black</item>
        </style>

    第二种 需要在styles.xml文件里,设置 android:windowFullscreen 属性:

        <style name="dialogFullScreen" parent="Theme.AppCompat.Dialog">
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="android:windowBackground">@android:color/white</item>
            <item name="android:textColor">@android:color/black</item>
            <item name="android:windowFullscreen">true</item>
        </style>

    以上2种互为互补,都可以实现需要的效果

    然后依然是重写DialogFragment

    public class MyDialog3 extends DialogFragment {
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false);
            return view;
        @Override
        public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
        @Override
        public void onStart() {
            WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
            params.width = ViewGroup.LayoutParams.MATCH_PARENT;//设置宽度为铺满
            params.gravity = Gravity.BOTTOM;
            getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params);
            super.onStart();
    

    然后是重点,在创建DialogFragment对话框的时候添加我们的style。

            mBtnTest.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    MyDialog3 myDialog = new MyDialog3();
                    myDialog.setStyle(DialogFragment.STYLE_NORMAL, R.style.dialogFullScreen);//添加上面创建的style
                    myDialog.show(getSupportFragmentManager(),"aa");
    

    下方的效果图里,我们就获得了一个在宽度上铺满屏幕的对话框,举一反三在设置高度上也是一样的:

    设置点击外部空白处不会关闭对话框

      在前面创建的对话框里,在点击外部后依然会关闭对话框,我们有时候有些重要消息并不希望用户可以点击外部可以取消。

    这个属性一样在styles.xml,创建style里添加 <item name="android:windowCloseOnTouchOutside">false</item>

        <style name="dialogFullScreen" parent="Theme.AppCompat.Dialog">
            <item name="android:padding">0dp</item>
            <item name="android:windowBackground">@android:color/white</item>
            <item name="android:textColor">@android:color/black</item>
            <item name="android:windowCloseOnTouchOutside">false</item>
        </style>
            //getDialog().setCancelable(false);//这个会屏蔽掉返回键
            getDialog().setCanceledOnTouchOutside(isCanceledOnTouchOutside());

    设置在弹出对话框后同时弹出软键盘

     我只需要两步,1.将需要输入内容的EditText设置为焦点 2.设置软键盘可见

    public class MyDialog3 extends DialogFragment {
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false);
            return view;
        @Override
        public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
            EditText editPassword = view.findViewById(R.id.edit_password);
            editPassword.requestFocus();//设置焦点
            getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);//设置输入盘可见
            super.onViewCreated(view, savedInstanceState);
        @Override
        public void onStart() {
            WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
            params.width = ViewGroup.LayoutParams.MATCH_PARENT;
            params.gravity = Gravity.BOTTOM;
            getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params);
            super.onStart();
    

    在Fragment里启动对话框

    与activity里一样没啥区别,唯一的区别就是你打算依然用老套的onActivityResult来向下传值,那么你就需要设置一个目标Fragment在下面的代码里setTargetFragment()方法就是起到这个作用的,在下面的代码里我们用MyDialog1 启动了 MyDialog2。

    DialogFragment其实就是Fragment,所以我这里就偷懒一下,直接用对话框启动对话框了。。。不在单独写一个Fragment

    MyDialog1.Java

    public class MyDialog1 extends DialogFragment {
        @NonNull
        @Override
        public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
            AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
            dialog.setTitle("测试Dialog");
            dialog.setMessage("启动另外一个对话框");
            dialog.setPositiveButton("启动", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    MyDialog2 myDialog2 = new MyDialog2();
                    myDialog2.setTargetFragment(MyDialog1.this, 300);
                    myDialog2.setStyle(DialogFragment.STYLE_NORMAL, R.style.dialogFullScreen);
                    myDialog2.show(getFragmentManager(), "myDialog2");
            return dialog.create();
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
        @Override
        public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            //这里可以返回 MyDialog2  Fragment的数据
    

    MyDialog2.Java

    public class MyDialog2 extends DialogFragment {
        private static final String TAG = "MyDialog";
        private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false);
            return view;
        @Override
        public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
        @Override
        public void onStart() {
            WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
            params.width = ViewGroup.LayoutParams.MATCH_PARENT;
            params.gravity = Gravity.BOTTOM;
            getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params);
            super.onStart();
    

    设置圆角与实际不符合的问题

    实际上是你设置的背景图片,被Dialog自带的背景遮盖了,导致圆角无法显示。所以设置一下透明背景就可以了。

    注意设置DecorView的背景与设置Window的背景是有区别的,区别如下:

    设置DecorView背景

        @Override
        public void onStart() {
            WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
            params.width = ViewGroup.LayoutParams.MATCH_PARENT;
            getDialog().getWindow().setAttributes(params);
            getDialog().getWindow().getDecorView().setBackground(new ColorDrawable(Color.TRANSPARENT));
    super.onStart();

    设置Window的背景

        @Override
        public void onStart() {
            WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
            params.width = ViewGroup.LayoutParams.MATCH_PARENT;
            getDialog().getWindow().setAttributes(params);
            getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            super.onStart();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
            layoutParams.dimAmount = 0f;//调整透明度
            getWindow().setAttributes(layoutParams);