zoukankan      html  css  js  c++  java
  • 手把手带你玩转 DialogFragment

    思维导图

     

    一、为什么要学习 DialogFragment

    你还在用 Dialog 吗?你还在经常烦恼于屏幕翻转的时候,Dialog 的各种奇葩情况吗?你想降低耦合吗?如果你有其中的一个烦恼,那么恭喜你,遇见了 DialogFragment ,他恰巧就解决了上面所说的问题,如果感兴趣的话,随笔者来看下吧!

     

    二、背景

    Android 官方推荐使用 DialogFragment 来代替 Dialog ,可以让它具有更高的可复用性(降低耦合)和更好的便利性(很好的处理屏幕翻转的情况)。而创建 DialogFragment 有两种方式:

    「法一:覆写其 onCreateDialog 方法」

    一般用于创建替代传统的 Dialog 对话框的场景,UI 简单,功能单一,不适用于使用了多线程(例如网络请求)的情况下(因为不能正确的获取当前 Fragment 的状态,会产生空指针异常)

    「法二:覆写其 onCreateView 方法」

    一般用于创建复杂内容弹窗或全屏展示效果的场景,UI 复杂,功能复杂,一般有网络请求等异步操作

     

    三、应用

    3.1 基本用法是什么

    法一:

    a.创建一个简单的 Dialog 并返回它即可

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        // 设置主题的构造方法
        // AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomDialog);
        builder.setTitle("注意:")
               .setMessage("是否退出应用?")
               .setPositiveButton("确定", null)
               .setNegativeButton("取消", null)
               .setCancelable(false);
               //builder.show(); // 不能在这里使用 show() 方法
        return builder.create();
    }
    

     

    b.你也可以使用自定义 View 来创建:

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        // 设置主题的构造方法
        // AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomDialog);
        LayoutInflater inflater = getActivity().getLayoutInflater();  
        View view = inflater.inflate(R.layout.fragment_dialog, null);  
        builder.setView(view) 
        // Do Someting,eg: TextView tv = view.findViewById(R.id.tv);
        return builder.create();
    }

     

    PS:创建 Dialog 的方式有多种,比如下面这种,使用时略有差异,需要自己注意:

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View view = inflater.inflate(R.layout.fragment_dialog, null);
        Dialog dialog = new Dialog(getActivity());
        // 设置主题的构造方法
        // Dialog dialog = new Dialog(getActivity(), R.style.CustomDialog);
        dialog.setContentView(view);
        // Do Someting
        return dialog;
    }

     

    这种情况,标题内容上面的白色部分,其实是默认的标题栏,如果需要的话,可以设置隐藏标题栏(将在下文说到)

    3.2 如何处理屏幕翻转

    如果使用传统的 Dialog ,需要我们手动处理屏幕翻转的情况,但使用 DialogFragment 的话,则不需要我们进行任何处理,FragmentManager 会自动管理 DialogFragment 的生命周期。

    3.3 如何隐藏标题栏

    在基本用法里代码注释有设置主题的地方,下面详细说下两种方法下设置无标题栏的方式:法一:

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        LayoutInflater inflater = Objects.requireNonNull(getActivity()).getLayoutInflater();
        @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.fragment_i_o_s_dialog, null);
        Dialog dialog = new Dialog(getActivity());
        // 关闭标题栏,setContentView() 之前调用
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(view);
        dialog.setCanceledOnTouchOutside(true);
        return dialog;
    }
    

    法二:

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setStyle(DialogFragment.STYLE_NO_TITLE, 0);
    }

    3.4 如何实现全屏

    常用的形式大多是宽度上和屏幕一样宽,高度自适应,下面直接看代码:

    法一:

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View view = inflater.inflate(R.layout.fragment_dialog, null);
        Dialog dialog = new Dialog(getActivity(), 0);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(view);
        dialog.setCanceledOnTouchOutside(true);
        //Do something
        // 设置宽度为屏宽、位置靠近屏幕底部
        Window window = dialog.getWindow();
        //设置了窗口的背景色为透明,这一步是必须的
        // <color name="transparent">#50000000</color>
        window.setBackgroundDrawableResource(R.color.transparent);
        WindowManager.LayoutParams wlp = window.getAttributes();
        wlp.gravity = Gravity.BOTTOM;
        //设置窗口的宽度为 MATCH_PARENT,效果是和屏幕宽度一样大
        wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
        wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
        window.setAttributes(wlp);
        return dialog;
    }
    

    法二:

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(DialogFragment.STYLE_NO_TITLE, 0);
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            getDialog().setCanceledOnTouchOutside(true);
            View rootView = inflater.inflate(R.layout.fragment_dialog, container, false);
            //Do something
            // 设置宽度为屏宽、靠近屏幕底部。
            final Window window = getDialog().getWindow();
         //这步是必须的
            window.setBackgroundDrawableResource(R.color.transparent);
         //必要,设置 padding,这一步也是必须的,内容不能填充全部宽度和高度
            window.getDecorView().setPadding(0, 0, 0, 0);
            WindowManager.LayoutParams wlp = window.getAttributes();
            wlp.gravity = Gravity.BOTTOM;
            wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
            wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
            window.setAttributes(wlp);
            return rootView;
    }
    

    3.5 应用场景的区别是什么

    文章一开始简单总结了法一 和法二的应用场景,这里说明下:

    法一:为简单的替代 Dialog 提供了非常方便的创建方式,但是在使用了多线程(例如网络请求)的情况下,不能正确的获取当前 Fragment 的状态,会产生空指针异常法二:则没有如上空指针的问题,而且,其创建方式默认使用了自定义 View,更便于应对复杂 UI 的场景

    3.6 如何与 Activity 进行交互?

    使用回调的方式

    a.在 DialogFragment 中:

    public interface OnDialogListener {
        void onDialogClick(String person);
    }
    
    private OnDialogListener mlistener;
    
    public void setOnDialogListener(OnDialogListener dialogListener){
        this.mlistener = dialogListener;
    }
    

    在 DialogFragment 的点击事件中:

    public OnDialogListener mlistener;
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.tv1:
                mlistener.onDialogClick("1");
                dismiss();
                break; 
           case R.id.tv2:
                mlistener.onDialogClick("2");
                dismiss();
                break;
            case R.id.tv3:
                mlistener.onDialogClick("3");
                dismiss();
                break;
            case R.id.tv4:
                mlistener.onDialogClick("4");
                dismiss();
                break;
        }
    }
    

    b.在 Activity 中

    dialogFragment.setOnDialogListener(new PersonDialogFragment.OnDialogListener() {
        @Override
        public void onDialogClick(String person) {
            ToastUtil.showToast(person);
        }
    });
    

    ####3.7 如何结合动画使用 a.设置从下到上弹出的动画

    private void slideToUp(View view) {
        Animation slide = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 0);
        slide.setDuration(400);
        slide.setFillEnabled(true);
        slide.setFillAfter(true);
        view.startAnimation(slide);
    }
    

    b.设置从上到下弹出的动画

    private boolean isAnimation = false;//用来判断是否多次点击。防止多次执行
    
    public void slideToDown(View view) {
        Animation slide = new TranslateAnimation(
                Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.0f, 
                Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 1.0f);
        slide.setDuration(400);
        slide.setFillEnabled(true);
        slide.setFillAfter(true);
        view.startAnimation(slide);
        slide.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }
    
            @Override
            public void onAnimationEnd(Animation animation) {
                //用来判断是否多次点击。防止多次执行
                isAnimation = false;
                //弹框消失
                IOSDialogFragment.this.dismiss();
            }
    
            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
    }
    

    c.封装从上到下弹出的动画

    加上判断是否多次点击。防止多次执行

    private void dialogFinish() {
        if (isAnimation) {
            return;
        }
        isAnimation = true;
        slideToDown(rootView);
    }

    3.8 如何在 Activity 弹出 DialogFragment ?

    mBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            IOSDialogFragment fragment = new IOSDialogFragment();
            //第二个参数是 tag
            fragment.show(getSupportFragmentManager(), "android");
        }
    });

    3.9 如何点击空白处时关闭的时候,还能使用动画?

    直接对 DecorView 设置 onTouchListener

    window.getDecorView().setOnTouchListener(new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_UP) {
                //弹框消失的动画执行相关代码
                ....
                ....
            
            }
            return true;
        }
    });
    

    四、结语

    终于看完了鸭!累死鸭了!如果还有什么不是很清楚的话,可以看下笔者写的示例 Demo https://github.com/LoveLifeEveryday/TestDialogFragment

    如果文章对您有一点帮助的话,希望您能点一下赞,您的点赞,是我前进的动力推荐阅读:

    Flutter在游戏开发的表现及跨平台带来的优势JVM 初级面试题【译】Flutter vs React Native vs Native:深度性能比较Flutter动画入门—— 双环Loading的实现锦囊篇|一文摸懂LeakCanary

    作者 | 许朋友爱玩

    地址 | juejin.im/post/6854573211854733320

    觉得不错的话,点个关注谢谢~

    点击链接《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

  • 相关阅读:
    posix多线程有感线程高级编程(线程属性函数总结)
    posix多线程有感线程高级编程(线程属性pthread_attr_t)
    posix多线程有感线程高级编程(条件变量属性)
    posix多线程有感线程高级编程(线程属性pthread_attr_t)实时调度
    posix多线程有感自旋锁
    posix多线程有感线程高级编程(线程属性函数总结)(代码)
    寒假Day58:javaTreeSetComparator
    Day62:java多线程
    寒假Day58:蓝桥杯2n皇后问题八皇后变形
    寒假Day57:CodeForces1325C Ehab and Pathetic MEXs树统计度思维
  • 原文地址:https://www.cnblogs.com/xiaoyusheng/p/13470105.html
Copyright © 2011-2022 走看看