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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。