Android开发 DialogFragment对话框详解

您所在的位置:网站首页 关闭dialog对话框 Android开发 DialogFragment对话框详解

Android开发 DialogFragment对话框详解

2024-01-22 00:18| 来源: 网络整理| 查看: 265

前言

  在聊DialogFragment之前,我们看看以往我们在Android里实现一个对话框一般有这几种方式:

  Dialog 继承重写Dialog实现一个自定义的Dialog   AlertDialog Android原生提供的对话框(底层是继承Dialog实现)   PopupWindow 用弹出悬浮框,实现对话框。这种对话框可以用在指定位置显示,一般用于一些非常小的按键弹窗。怎么实现可以参考我的博客:https://www.cnblogs.com/guanxinjing/p/10156153.html

  这3种弹窗对话框都有一个问题,就是与activity的生命周期不是捆绑的,得时刻注意在activity后台之后关闭Dialog。所以,后面google推荐使用DialogFragment来取代它们。DialogFragment本质其实是Fragment,有Fragment的生命周期并且与创建它的activity有捆绑。

  在google推出了Jetpack系列后,DialogFragment配合Jetpack系列的ViewModel、LiveData与Navigation在使用上比一般的Dialog更加安全。并且在数据传递上也非常简单,配合Navigation架构管理起来也十分简单明晰。如果你未接触过不了解Navigation,可以我的参考博客:Android开发 navigation入门详解

  下面我们就根据2个最简单demo和与一些使用特例,来介绍DialogFragment的使用。

以Dialog创建DialogFragment的简单Demo

  DialogFragment有2种方法创建我们需要的对话框内容,其中就有以Dialog来创建内容方式。

继承重写DialogFragment: 

public class MyDialog1 extends DialogFragment { @NonNull @Override public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { //创建对话框,我们需要返回dialog AlertDialog.Builder dialog = new AlertDialog.Builder(getContext()); dialog.setTitle("测试Dialog"); dialog.setMessage("DialogFragment"); return dialog.create(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); //此方法在视图已经创建后返回的,但是这个view 还没有添加到父级中,我们在这里可以重新设定view的各个数据 } }

在activity里显示对话框:

这里的show传入的是activity的getSupportFragmentManager(),这因为DialogFragment本质上是Fragment,它需要通过Activity的FragmentManager进行管理。此外这里的第二个参数tag,其实也是用于管理Fragment的tag。

mBtnTest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myDialog = new MyDialog1(); myDialog.show(getSupportFragmentManager(),"myDialog"); } });

效果图:

以布局View创建DialogFragment的简单Demo

  DialogFragment另一种创建内容方法,导入一个View

public class MyDialog extends DialogFragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false); return view; } @Override public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); /* 此方法在视图View已经创建后返回的,但是这个view 还没有添加到父级中。 我们在这里可以重新设定view的各个数据,但是不能修改对话框最外层的ViewGroup的布局参数。 因为这里的view还没添加到父级中,我们需要在下面onStart生命周期里修改对话框尺寸参数 */ } @Override public void onStart() { /* 因为View在添加后,对话框最外层的ViewGroup并不知道我们导入的View所需要的的宽度。 所以我们需要在onStart生命周期里修改对话框尺寸参数 */ WindowManager.LayoutParams params = getDialog().getWindow().getAttributes(); params.width = ViewGroup.LayoutParams.MATCH_PARENT; getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params); super.onStart(); } }

显示对话框的代码跟上面的demo一样,就不重复贴出来了,看看效果图:

设置对话框在屏幕中的显示位置 public class MyDialog2 extends DialogFragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false); return view; } @Override public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); } @Override public void onStart() { WindowManager.LayoutParams params = getDialog().getWindow().getAttributes(); params.width = ViewGroup.LayoutParams.MATCH_PARENT; params.gravity = Gravity.BOTTOM; //将对话框放到布局下面,也就是屏幕下方 getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params); super.onStart(); } }

效果图:

将对话框的宽或者高铺满屏幕

设置对话框铺满屏幕有2种方式:

第一种  需要在styles.xml文件里,添加一个没有内边距的style,如下:

  在上面的出现在屏幕下方的对话框中,依然与屏幕有小段距离,那个其实是dialog自带的padding内边距属性导致的。这种方式可以设置只在宽度上铺满屏幕,但是高度上依然留有一定的内边距。

@color/colorPrimary @color/colorPrimaryDark @color/colorAccent 0dp @android:color/white @android:color/black

第二种 需要在styles.xml文件里,设置 android:windowFullscreen 属性:

@color/colorPrimary @color/colorPrimaryDark @color/colorAccent @android:color/white @android:color/black true

以上2种互为互补,都可以实现需要的效果

然后依然是重写DialogFragment

public class MyDialog3 extends DialogFragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false); return view; } @Override public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); } @Override public void onStart() { WindowManager.LayoutParams params = getDialog().getWindow().getAttributes(); params.width = ViewGroup.LayoutParams.MATCH_PARENT;//设置宽度为铺满 params.gravity = Gravity.BOTTOM; getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params); super.onStart(); } }

然后是重点,在创建DialogFragment对话框的时候添加我们的style。

mBtnTest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MyDialog3 myDialog = new MyDialog3(); myDialog.setStyle(DialogFragment.STYLE_NORMAL, R.style.dialogFullScreen);//添加上面创建的style myDialog.show(getSupportFragmentManager(),"aa"); } });

下方的效果图里,我们就获得了一个在宽度上铺满屏幕的对话框,举一反三在设置高度上也是一样的:

设置点击外部空白处不会关闭对话框 方式一

  在前面创建的对话框里,在点击外部后依然会关闭对话框,我们有时候有些重要消息并不希望用户可以点击外部可以取消。

这个属性一样在styles.xml,创建style里添加 false

0dp @android:color/white @android:color/black false 方式二 //getDialog().setCancelable(false);//这个会屏蔽掉返回键 getDialog().setCanceledOnTouchOutside(isCanceledOnTouchOutside()); 设置在弹出对话框后同时弹出软键盘

 我只需要两步,1.将需要输入内容的EditText设置为焦点 2.设置软键盘可见

public class MyDialog3 extends DialogFragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false); return view; } @Override public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) { EditText editPassword = view.findViewById(R.id.edit_password); editPassword.requestFocus();//设置焦点 getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);//设置输入盘可见 super.onViewCreated(view, savedInstanceState); } @Override public void onStart() { WindowManager.LayoutParams params = getDialog().getWindow().getAttributes(); params.width = ViewGroup.LayoutParams.MATCH_PARENT; params.gravity = Gravity.BOTTOM; getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params); super.onStart(); } }

效果图:

在Fragment里启动对话框

与activity里一样没啥区别,唯一的区别就是你打算依然用老套的onActivityResult来向下传值,那么你就需要设置一个目标Fragment在下面的代码里setTargetFragment()方法就是起到这个作用的,在下面的代码里我们用MyDialog1 启动了 MyDialog2。

DialogFragment其实就是Fragment,所以我这里就偷懒一下,直接用对话框启动对话框了。。。不在单独写一个Fragment

MyDialog1.Java

public class MyDialog1 extends DialogFragment { @NonNull @Override public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { AlertDialog.Builder dialog = new AlertDialog.Builder(getContext()); dialog.setTitle("测试Dialog"); dialog.setMessage("启动另外一个对话框"); dialog.setPositiveButton("启动", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { MyDialog2 myDialog2 = new MyDialog2(); myDialog2.setTargetFragment(MyDialog1.this, 300); myDialog2.setStyle(DialogFragment.STYLE_NORMAL, R.style.dialogFullScreen); myDialog2.show(getFragmentManager(), "myDialog2"); } }); return dialog.create(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); } @Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); //这里可以返回 MyDialog2 Fragment的数据 } }

MyDialog2.Java

public class MyDialog2 extends DialogFragment { private static final String TAG = "MyDialog"; private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false); return view; } @Override public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); } @Override public void onStart() { WindowManager.LayoutParams params = getDialog().getWindow().getAttributes(); params.width = ViewGroup.LayoutParams.MATCH_PARENT; params.gravity = Gravity.BOTTOM; getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params); super.onStart(); } } 设置圆角与实际不符合的问题

实际上是你设置的背景图片,被Dialog自带的背景遮盖了,导致圆角无法显示。所以设置一下透明背景就可以了。

注意设置DecorView的背景与设置Window的背景是有区别的,区别如下:

设置DecorView背景 @Override public void onStart() { WindowManager.LayoutParams params = getDialog().getWindow().getAttributes(); params.width = ViewGroup.LayoutParams.MATCH_PARENT; getDialog().getWindow().setAttributes(params); getDialog().getWindow().getDecorView().setBackground(new ColorDrawable(Color.TRANSPARENT)); super.onStart(); }

效果图:

设置Window的背景 @Override public void onStart() { WindowManager.LayoutParams params = getDialog().getWindow().getAttributes(); params.width = ViewGroup.LayoutParams.MATCH_PARENT; getDialog().getWindow().setAttributes(params); getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); super.onStart(); }

 

改变Window背景的透明度 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); WindowManager.LayoutParams layoutParams = getWindow().getAttributes(); layoutParams.dimAmount = 0f;//调整透明度 getWindow().setAttributes(layoutParams); } 个人封装的BaseDialogFragment类

将对话框显示比例、对话框状态栏显示、状态栏边框封装处理、当然你也可以通过上面的styles.xml的方式进行封装处理。个人比较喜欢用代码配置,这样方便复制到其他项目中使用。

import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.view.View import android.view.WindowManager import androidx.fragment.app.DialogFragment /** * DialogFragment基础对话框 */ abstract class BaseDialogFragment : DialogFragment() { private var mWidthRatio = 1f private var mHeightRatio = 1f private var mIsShowNavigation = false /** * 设置对话框宽度与屏幕宽度占比 0.0~1.0 * * @param widthRatio */ open fun setWidthHeightRatio(widthRatio: Float, heightRation: Float) { mWidthRatio = widthRatio mHeightRatio = heightRation } /** * 设置是否显示对话框 */ open fun setShowNavigation(isShowNavigation: Boolean) { mIsShowNavigation = isShowNavigation } override fun onStart() { super.onStart() dialog?.window?.let { window -> //隐藏导航栏 if (!mIsShowNavigation) { // 隐藏导航栏并禁止显示 val decorView: View = window.decorView val uiOptions = (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) decorView.systemUiVisibility = uiOptions } //去除对话框内边距与背景透明 context?.let { context -> val layoutParams: WindowManager.LayoutParams = window.getAttributes() if (mWidthRatio == 1f) { layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT } else { val width = context.resources.displayMetrics.widthPixels * mWidthRatio layoutParams.width = width.toInt() } if (mHeightRatio == 1f) { layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT } else { val height = context.resources.displayMetrics.heightPixels * mHeightRatio layoutParams.height = height.toInt() } window.attributes = layoutParams } //透明背景 window.decorView.background = ColorDrawable(Color.TRANSPARENT) //去除边距 window.decorView.setPadding(0, 0, 0, 0) } } }

使用它的例子代码:

class SelectDialog(val childId: Int) : BaseDialogFragment() { private lateinit var mBinding: MainDialogSelectBinding private val mViewModel by lazy { activity?.let { ViewModelProvider(it).get(MainViewModel::class.java) } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { mBinding = MainDialogSelectBinding.inflate(inflater) return mBinding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setWidthHeightRatio(0.6f, 0.76f) mBinding.content.text = "是否删除孩子数据?" mBinding.cancel.setOnClickListener { dismiss() } mBinding.submit.setOnIntervalClickListener { mViewModel?.deleteChild(childId) } } }

 activity中显示

SelectDialog(it.id).show(supportFragmentManager, "ChildDeleteConfirmDialog")

 

end



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3