本文已经收录到个人 Github 我的博客,欢迎大佬们光临寒舍:android
个人 GIthub 博客ios
你还在用 Dialog
吗?git
你还在常常烦恼于屏幕翻转的时候,Dialog
的各类奇葩状况吗?github
你想下降耦合吗?web
若是你有其中的一个烦恼,那么恭喜你,碰见了 DialogFragment
,他恰巧就解决了上面所说的问题,若是感兴趣的话,随笔者来看下吧!网络
Android 官方推荐使用 DialogFragment
来代替 Dialog
,可让它具备更高的可复用性(下降耦合)和更好的便利性(很好的处理屏幕翻转的状况)。多线程
而建立 DialogFragment
有两种方式:异步
onCreateDialog
方法
通常用于建立替代传统的
Dialog
对话框的场景,UI 简单,功能单一,不适用于使用了多线程(例如网络请求)的状况下(由于不能正确的获取当前Fragment
的状态,会产生空指针异常)编辑器
onCreateView
方法
通常用于建立复杂内容弹窗或全屏展现效果的场景,UI 复杂,功能复杂,通常有网络请求等异步操做ide
法一:
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; } 复制代码
这种状况,标题内容上面的白色部分,实际上是默认的标题栏,若是须要的话,能够设置隐藏标题栏(将在下文说到)
若是使用传统的 Dialog
,须要咱们手动处理屏幕翻转的状况,但使用 DialogFragment
的话,则不须要咱们进行任何处理,FragmentManager
会自动管理 DialogFragment
的生命周期。
在基本用法里代码注释有设置主题的地方,下面详细说下两种方法下设置无标题栏的方式:
法一:
@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); } 复制代码
经常使用的形式大可能是宽度上和屏幕同样宽,高度自适应,下面直接看代码:
法一:
@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; } 复制代码
文章一开始简单总结了法一 和法二的应用场景,这里说明下:
Dialog
提供了很是方便的建立方式,可是在使用了多线程(例如网络请求)的状况下,不能正确的获取当前
Fragment
的状态,会产生空指针异常
View
,更便于应对复杂
UI
的场景
使用回调的方式
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); } }); 复制代码
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); } 复制代码
mBtn.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) { IOSDialogFragment fragment = new IOSDialogFragment(); //第二个参数是 tag fragment.show(getSupportFragmentManager(), "android"); } }); 复制代码
直接对
DecorView
设置onTouchListener
window.getDecorView().setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { //弹框消失的动画执行相关代码 .... .... } return true; } }); 复制代码
终于看完了鸭!累死鸭了!若是还有什么不是很清楚的话,能够看下笔者写的示例 Demo
若是文章对您有一点帮助的话,但愿您能点一下赞,您的点赞,是我前进的动力
本文参考连接:
本文使用 mdnice 排版