项目中经常使用弹窗,有些业务我都喜欢封装在Dialog中统一实现,避免在activty/fragment层中搞得乱七八糟的,这里主要记录一下我日常的Dialog封装
弹窗类型
我主要使用的是这几种:
- Dialog
- DialogFragment
- BottoSheet
一些演示效果:
Dialog封装
Dialog其实用的很少,大部分是DialogFragment,这里就简单使用Viewbinding封装了下
abstract class BaseDialog<VB : ViewBinding>(
context: Context,
themeResId: Int,
private val inflate: (LayoutInflater) -> VB
) : Dialog(context, themeResId) {
lateinit var binding: VB
constructor(context: Context, inflate: (LayoutInflater) -> VB) : this(context, 0, inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = inflate(layoutInflater)
setContentView(binding.root)
}
}
DialogFragment封装
首先需要明确封装的意义,就是为了在使用的时候更加方便,省略模板代码。
- 使用viewbinding
- dialog参数初始化:大小,透明度,位置,动画等
- 点击事件监听
/**
* 基础Dialog封装
*/
abstract class BaseDialogFragment<VB : ViewBinding>(
private val inflate: (LayoutInflater, ViewGroup?, Boolean) -> VB
) : DialogFragment() {
val TAG by lazy {
this.javaClass.name
}
private var _binding: VB? = null
val binding: VB get() = _binding!!
private var isLoaded = false
lateinit var mContext: Context
private var cancel:Boolean = true
private var gravity:Int = Gravity.CENTER
private var width:Int = WRAP_CONTENT
private var height:Int = WRAP_CONTENT
@StyleRes
private var animation: Int = R.style.dialogAnimation_center
@StyleRes
private var style:Int = R.style.DialogThemeTrans
@DrawableRes
private var layoutBackground:Int = android.R.color.transparent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, style)
}
override fun onStart() {
super.onStart()
dialog?.window?.apply {
setWindowAnimations(animation)
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialog?.apply {
setCancelable(cancel)
setCanceledOnTouchOutside(cancel)
isCancelable = cancel
}
dialog?.window?.apply {
setLayout(width,height)
setGravity(gravity)
setBackgroundDrawableResource(layoutBackground)
}
}
override fun onResume() {
super.onResume()
lifecycleScope.launch {
initView()
initData()
}
}
fun initParams(cancel:Boolean = this.cancel,
width:Int = this.width,
height:Int = this.height,
@DrawableRes layoutBackground:Int = this.layoutBackground,
gravity: Int = this.gravity,
@StyleRes anim:Int = this.animation,
@StyleRes style:Int = this.style
) = apply{
this.animation = anim
this.style = style
this.cancel = cancel
this.width = width
this.height = height
this.layoutBackground = layoutBackground
this.gravity = gravity
}
abstract suspend fun initView()
abstract suspend fun initData()
override fun onDestroy() {
_binding = null
lifecycleScope.cancel()
super.onDestroy()
}
}
使用:还是非常简单的,使用lifecycleScope来执行逻辑代码函数,创建只需要在init中初始化各种参数就行
实例:
class BottomDialog : BaseDialogFragment<DialogTestBinding>(DialogTestBinding::inflate) {
companion object{
@JvmStatic
fun getInstance() = BottomDialog()
}
init {
//进行初始化参数配置
initParams(
cancel = false,
gravity = Gravity.BOTTOM,
width = ViewGroup.LayoutParams.MATCH_PARENT,
height = 300.dp.toInt(),
layoutBackground = R.drawable.dialog_bottom_back,
anim = R.style.dialogAnimation_bottom,
style = R.style.DialogThemeShadow
)
}
override suspend fun initView() {
}
override suspend fun initData() {
}
}
关于主题和动画配置,在style中设置背景透明等。。。
<!--背景透明的dialog-->
<style name="DialogThemeTrans" parent="android:Animation">
<!-- Dialog以外的区域遮盖效果 -->
<item name="android:backgroundDimEnabled">false</item>
<!-- 无标题 -->
<item name="android:windowNoTitle">true</item>
<!-- 边框 -->
<item name="android:windowFrame">@null</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<!--带阴影的Dialog-->
<style name="DialogThemeShadow" parent="android:Animation">
<!-- Dialog以外的区域遮盖效果 -->
<item name="android:backgroundDimEnabled">true</item>
<!-- 无标题 -->
<item name="android:windowNoTitle">true</item>
<!-- 边框 -->
<item name="android:windowFrame">@null</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
关于动画,需要在onStart中设置:
override fun onStart() {
super.onStart()
dialog?.window?.apply {
setWindowAnimations(animation)
}
}
<!--dialog动画:中心渐入-->
<style name="dialogAnimation_center">
<item name="android:windowEnterAnimation">@anim/dialog_in_center</item>
<item name="android:windowExitAnimation">@anim/dialog_out_center</item>
</style>
<!--dialog动画:底部进入-->
<style name="dialogAnimation_bottom">
<item name="android:windowEnterAnimation">@anim/dialog_in_bottom</item>
<item name="android:windowExitAnimation">@anim/dialog_out_bottom</item>
</style>
关于具体的动画代码在Demo中查看
BottomSheet
BottomSheetDialogFragment继承自AppCompatDialogFragment,最终实现也是DialogFragment,所以一些参数上设置是相同的,主要是折叠初始化设置:
abstract class BaseBottomSheetDialogFragment<VB : ViewBinding>(
private val inflate: (LayoutInflater, ViewGroup?, Boolean) -> VB
) : BottomSheetDialogFragment() {
private val TAG by lazy {
this.javaClass.name
}
lateinit var mContext: Context
private var _binding: VB? = null
val binding: VB get() = _binding!!
//是否展示背景阴影
var showShadow = false
//是否能否点击外部取消
var cancel = true
//默认状态:折叠
var defaultState = BottomSheetBehavior.STATE_COLLAPSED
//默认折叠高度
var peekHeight:Int = 100.dp.toInt()
//布局默认背景
@DrawableRes
var layoutBackground:Int = android.R.color.transparent
@StyleRes
var animation: Int = R.style.dialogAnimation_bottom
@StyleRes
var style:Int = R.style.DialogThemeShadow
var width:Int = ViewGroup.LayoutParams.WRAP_CONTENT
var height:Int = ViewGroup.LayoutParams.WRAP_CONTENT
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL,style)
}
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context
}
override fun onStart() {
super.onStart()
dialog?.window?.apply {
setWindowAnimations(animation)
if (!showShadow){
val windowParams: WindowManager.LayoutParams = attributes
windowParams.dimAmount = 0f
attributes = windowParams
}
}
//拿到系统的 bottom_sheet
val view: FrameLayout = dialog?.findViewById(R.id.design_bottom_sheet)!!
//设置view高度
view.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
//获取behavior
val behavior = BottomSheetBehavior.from(view)
//设置弹出高度
behavior.peekHeight = peekHeight
//设置展开状态
//behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
when (newState) {
BottomSheetBehavior.STATE_EXPANDED -> {
}
BottomSheetBehavior.STATE_COLLAPSED -> {
}
BottomSheetBehavior.STATE_DRAGGING -> {
}
BottomSheetBehavior.STATE_SETTLING -> {
}
BottomSheetBehavior.STATE_HIDDEN -> {
}
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
})
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialog?.apply {
setCancelable(cancel)
setCanceledOnTouchOutside(cancel)
isCancelable = cancel
}
dialog?.window?.apply {
setLayout(width,height)
setBackgroundDrawableResource(layoutBackground)
}
}
override fun onResume() {
super.onResume()
lifecycleScope.launch {
initView()
initData()
}
}
abstract suspend fun initData()
abstract suspend fun initView()
}
Dialog中的小问题还是挺多的,需要根据自己的需要配置,具体代码大家看这里: mvvm_develop快速开发框架 是我日常测试的代码合集,一些常用工具封装,或者jetpak组件测试等。卑微Androider求个Star
还有一些关于UI的基础工具: