手把手带你玩转 DialogFragment

前言

本文已经收录到个人 Github 我的博客,欢迎大佬们光临寒舍:android

个人 GIthub 博客ios

思惟导图

1、为何要学习 DialogFragment

你还在用 Dialog 吗?git

你还在常常烦恼于屏幕翻转的时候,Dialog 的各类奇葩状况吗?github

你想下降耦合吗?web

若是你有其中的一个烦恼,那么恭喜你,碰见了 DialogFragment ,他恰巧就解决了上面所说的问题,若是感兴趣的话,随笔者来看下吧!网络

2、背景

Android 官方推荐使用 DialogFragment 来代替 Dialog ,可让它具备更高的可复用性(下降耦合)和更好的便利性(很好的处理屏幕翻转的状况)。多线程

而建立 DialogFragment 有两种方式:异步

  • 法一:覆写其 onCreateDialog 方法

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

  • 法二:覆写其 onCreateView 方法

通常用于建立复杂内容弹窗全屏展现效果的场景,UI 复杂功能复杂通常有网络请求等异步操做ide

3、应用

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;  } });  复制代码

4、结语

终于看完了鸭!累死鸭了!若是还有什么不是很清楚的话,能够看下笔者写的示例 Demo


若是文章对您有一点帮助的话,但愿您能点一下赞,您的点赞,是我前进的动力

本文参考连接:

本文使用 mdnice 排版

相关文章
相关标签/搜索