热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

DialogFragment运行原理及使用方法详解

这篇文章主要介绍了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();
  //设置了窗口的背景色为透明,这一步是必须的
  // #50000000
  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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 吴石访谈:腾讯安全科恩实验室如何引领物联网安全研究
    腾讯安全科恩实验室曾两次成功破解特斯拉自动驾驶系统,并远程控制汽车,展示了其在汽车安全领域的强大实力。近日,该实验室负责人吴石接受了InfoQ的专访,详细介绍了团队未来的重点方向——物联网安全。 ... [详细]
  • 默认情况下,Git 使用 Nano 编辑器进行提交信息的编辑,但如果您更喜欢使用 Vim,可以通过简单的配置更改来实现这一变化。本文将指导您如何通过修改全局配置文件来设置 Vim 作为默认的 Git 提交编辑器。 ... [详细]
  • Fiddler 安装与配置指南
    本文详细介绍了Fiddler的安装步骤及配置方法,旨在帮助用户顺利抓取用户Token。文章还涵盖了一些常见问题的解决方案,以确保安装过程顺利。 ... [详细]
  • 在1995年,Simon Plouffe 发现了一种特殊的求和方法来表示某些常数。两年后,Bailey 和 Borwein 在他们的论文中发表了这一发现,这种方法被命名为 Bailey-Borwein-Plouffe (BBP) 公式。该问题要求计算圆周率 π 的第 n 个十六进制数字。 ... [详细]
  • 2023年,Android开发前景如何?25岁还能转行吗?
    近期,关于Android开发行业的讨论在多个平台上热度不减,许多人担忧其未来发展。本文将探讨当前Android开发市场的现状、薪资水平及职业选择建议。 ... [详细]
  • 基于SSM框架的在线考试系统:随机组卷功能详解
    本文深入探讨了基于SSM(Spring, Spring MVC, MyBatis)框架构建的在线考试系统中,随机组卷功能的设计与实现方法。 ... [详细]
  • binlog2sql,你该知道的数据恢复工具
    binlog2sql,你该知道的数据恢复工具 ... [详细]
  • Python3爬虫入门:pyspider的基本使用[python爬虫入门]
    Python学习网有大量免费的Python入门教程,欢迎大家来学习。本文主要通过爬取去哪儿网的旅游攻略来给大家介绍pyspid ... [详细]
  • 在Android中实现黑客帝国风格的数字雨效果
    本文将详细介绍如何在Android平台上利用自定义View实现类似《黑客帝国》中的数字雨效果。通过实例代码,我们将探讨如何设置文字颜色、大小,以及如何控制数字下落的速度和间隔。 ... [详细]
  • 本文详细介绍了如何在 Ubuntu 14.04 系统上搭建仅使用 CPU 的 Caffe 深度学习框架,包括环境准备、依赖安装及编译过程。 ... [详细]
  • 利用Node.js实现PSD文件的高效切图
    本文介绍了如何通过Node.js及其psd2json模块,快速实现PSD文件的自动化切图过程,以适应项目中频繁的界面更新需求。此方法不仅提高了工作效率,还简化了从设计稿到实际应用的转换流程。 ... [详细]
  • 七大策略降低云上MySQL成本
    在全球经济放缓和通胀压力下,降低云环境中MySQL数据库的运行成本成为企业关注的重点。本文提供了一系列实用技巧,旨在帮助企业有效控制成本,同时保持高效运作。 ... [详细]
  • 本文通过C++语言实现了一个递归算法,用于解析并计算数学表达式的值。该算法能够处理加法、减法、乘法和除法操作。 ... [详细]
  • 龙蜥社区开发者访谈:技术生涯的三次蜕变 | 第3期
    龙蜥社区的开发者们通过自己的实践和经验,推动着开源技术的发展。本期「龙蜥开发者说」聚焦于一位资深开发者的三次技术转型,分享他在龙蜥社区的成长故事。 ... [详细]
  • importjava.io.*;importjava.util.*;publicclass五子棋游戏{staticintm1;staticintn1;staticfinalintS ... [详细]
author-avatar
ao吖浩_257
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有