手把手带你玩转 DialogFragment 一、为什么要学习 DialogFragment 二、背景 三、应用 3.1 基本用法是什么 3.2 如何处理屏幕翻转 3.3 如何隐藏标题栏 3.4 如何实现全屏 3.5 应用场景的区别是什么 3.6 如何与 Activity 进行交互? 3.8 如何在 Activity 弹出 DialogFragment ? 3.9 如何点击空白处时关闭的时候,还能使用动画? 四、结语

思维导图

手把手带你玩转 DialogFragment
一、为什么要学习 DialogFragment
二、背景
三、应用
3.1 基本用法是什么
3.2 如何处理屏幕翻转
3.3 如何隐藏标题栏
3.4 如何实现全屏
3.5 应用场景的区别是什么
3.6 如何与 Activity 进行交互?
3.8 如何在 Activity 弹出 DialogFragment ?
3.9 如何点击空白处时关闭的时候,还能使用动画?
四、结语

 

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

手把手带你玩转 DialogFragment
一、为什么要学习 DialogFragment
二、背景
三、应用
3.1 基本用法是什么
3.2 如何处理屏幕翻转
3.3 如何隐藏标题栏
3.4 如何实现全屏
3.5 应用场景的区别是什么
3.6 如何与 Activity 进行交互?
3.8 如何在 Activity 弹出 DialogFragment ?
3.9 如何点击空白处时关闭的时候,还能使用动画?
四、结语

 

二、背景

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

「法一:覆写其 onCreateDialog 方法」

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

「法二:覆写其 onCreateView 方法」

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

手把手带你玩转 DialogFragment
一、为什么要学习 DialogFragment
二、背景
三、应用
3.1 基本用法是什么
3.2 如何处理屏幕翻转
3.3 如何隐藏标题栏
3.4 如何实现全屏
3.5 应用场景的区别是什么
3.6 如何与 Activity 进行交互?
3.8 如何在 Activity 弹出 DialogFragment ?
3.9 如何点击空白处时关闭的时候,还能使用动画?
四、结语

 

三、应用

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();
}
手把手带你玩转 DialogFragment
一、为什么要学习 DialogFragment
二、背景
三、应用
3.1 基本用法是什么
3.2 如何处理屏幕翻转
3.3 如何隐藏标题栏
3.4 如何实现全屏
3.5 应用场景的区别是什么
3.6 如何与 Activity 进行交互?
3.8 如何在 Activity 弹出 DialogFragment ?
3.9 如何点击空白处时关闭的时候,还能使用动画?
四、结语

 

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();
}
手把手带你玩转 DialogFragment
一、为什么要学习 DialogFragment
二、背景
三、应用
3.1 基本用法是什么
3.2 如何处理屏幕翻转
3.3 如何隐藏标题栏
3.4 如何实现全屏
3.5 应用场景的区别是什么
3.6 如何与 Activity 进行交互?
3.8 如何在 Activity 弹出 DialogFragment ?
3.9 如何点击空白处时关闭的时候,还能使用动画?
四、结语

 

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;
}
手把手带你玩转 DialogFragment
一、为什么要学习 DialogFragment
二、背景
三、应用
3.1 基本用法是什么
3.2 如何处理屏幕翻转
3.3 如何隐藏标题栏
3.4 如何实现全屏
3.5 应用场景的区别是什么
3.6 如何与 Activity 进行交互?
3.8 如何在 Activity 弹出 DialogFragment ?
3.9 如何点击空白处时关闭的时候,还能使用动画?
四、结语

 

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

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、微服务、数据库、数据结构等等。