zoukankan      html  css  js  c++  java
  • Android开发 DialogFragment对话框详解

    前言

      在聊DialogFragment之前,我们看看以往我们在Android里实现一个对话框一般有这几种方式:

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

     


     

    end

  • 相关阅读:
    gc buffer busy/gcs log flush sync与log file sync
    给Oracle年轻的初学者的几点建议
    Android 编程下帧动画在 Activity 启动时自动运行的几种方式
    Android 编程下 Touch 事件的分发和消费机制
    Java 编程下 static 关键字
    Java 编程下 final 关键字
    Android 编程下模拟 HOME 键效果
    Why Are Thread.stop, Thread.suspend, Thread.resume and Runtime.runFinalizersOnExit Deprecated ?
    Extjs4 大型项目目录结构重构
    [转]SQLServer 2008 允许远程连接的配置方法
  • 原文地址:https://www.cnblogs.com/guanxinjing/p/12044196.html
Copyright © 2011-2022 走看看