简单封装一个易拓展的Dialog

简介: 前者的封装司空见惯,大多数的公司也都是采取的这样的封装,毕竟调用者实现起来也是很方便,这里就不详细说了,具体我们谈一下后者的封装,后者的封装虽然调用者需要自己来实现,但是扩展性是很强的。

Dialog,每个项目中多多少少都会用到,肯定也会有自己的一套封装逻辑,无论如何封装,都是奔着简单复用的思想,有的是深层次的封装,也就是把相关的UI效果直接封装好,暴露可以修改的属性和方法,让调用者根据实际业务,调用修改即可,当然也有简单的封装,只封装基本的功能,其UI和实际的动作,交给调用者,两种封装方式,各有利弊,前者调用者不用自己创建UI和实现相关动作,只需要简单的调用即可,但是不易于扩展,效果比较局限,想要拓展其他的效果,就不得不自己动手实现;后者扩展性强,因为只提供基本的调用方式,也就是说,你想要什么效果都行,毕竟是所有的UI和动作都是你自己来实现,优点是它,其缺点也是它。


前者的封装司空见惯,大多数的公司也都是采取的这样的封装,毕竟调用者实现起来也是很方便,这里就不详细说了,具体我们谈一下后者的封装,后者的封装虽然调用者需要自己来实现,但是扩展性是很强的。


今天的内容大致如下:


1、效果及代码具体调用。

2、如何封装一个Dialog。

3、开源地址。

4、总结及注意事项。


一、效果及代码具体调用


通过Kotlin的扩展函数,参数以类做为扩展,封装之后,调用非常的便捷,只需要传递你要的视图即可,我们先看下具体的案例,代码如下:


showVipDialog {
addLayout(R.layout.layout_dialog_custom)//传递dialog视图set {
//Dialog操作,获取View及绑定数据                    }
                }


通过以上的代码,我们就实现了一个Dialog的弹出,addLayout方法传递视图,set扩展函数进行获取View和绑定数据,这样的一个简单的封装,我们就实现了Dialog的扩展操作,针对不同的Dialog样式,传递不同的xml视图即可。


1、快速使用


为了方便大家使用,目前已经上传到了远程maven,大家可以进行依赖使用,或者下载源码依赖也可以。


根项目build.gradle


allprojects {
repositories {
……maven { url"https://gitee.com/AbnerAndroid/almighty/raw/master" }
    }
}


在需要的Module下引入依赖


dependencies {
……implementation"com.vip:dialog:1.0.0"}


2、代码案例


源码下载之后,运行项目,就可以看到给大家提供的相关Demo,当然了,由于做到了可扩展,大家想实现什么样的效果都是可以的,毕竟视图都是自己传递的。


由于所有的案例都是调用开头的代码,就不一一列举了,简单的列举几个。


普通的提示框



普通的提示框,可以按照下面的代码逻辑进行调用。


showVipDialog {
addLayout(R.layout.layout_dialog_custom)//添加弹出的视图set {//逻辑处理,获取view,绑定数据setDialogCancelable(false)//点击空白不消失valbtnConfirm=findView<TextView>(R.id.dialog_button_confirm)//获取ViewbtnConfirm.setOnClickListener {
toast("确定")
dismiss()
        }
    }
}

方法一览


方法名

参数类型

概述

addLayout

int

xml视图

set

无参

逻辑处理

style

无参

dialog设置样式

setDialogCancelable

Boolean

点击空白是否消失,默认true消失,false为不消失

findView

int

控件id,泛型为控件

dismiss

无参

隐藏dialog

getDialogView

无参

获取当前View视图


DataBinding形式的提示框


DataBinding形式和普通的区别在于,不用再获取View视图,由普通的set扩展函数改为bind扩展函数,泛型为Binding,记得把xml视图进行convert to data binding layout。


showVipDialog {
addLayout(R.layout.layout_dialog_custom)//添加弹出的视图bind<LayoutDialogCustomBinding> {//逻辑处理,获取view,绑定数据it.dialogButtonConfirm.setOnClickListener {
toast("确定")
dismiss()
        }
    }
}


方法一览


除了普通的方法调用之外,还可以调用下面的方法。


bind

无参

和set一样进行逻辑处理,泛型为ViewDataBinding

getDataBinding

无参

获取当前的DataBinding,用于更新视图

setPendingBindings

int

传递的BR,用于xml和Data数据进行绑定


具体的案例大家直接可以看源码,源码中提供了很多常见的效果,都是可以自定义实现的,具体的就不罗列了,本身没有多少难度。


确认框



输入框



底部列表



菊花加载



二、如何封装一个Dialog


这样的一个简单的Dialog如何进行封装呢?在封装之前,我们首先要明确封装思路,1、视图由调用者传递,2、逻辑操作由调用者处理,3、样式也由调用者进行设置,也就是说,我们只封装基本的dialog使用,也就是一个壳,具体的内容,统统交给调用者进行处理,有了这三个思路我们就可以进行着手封装了。


1、封装BaseDialog


封装Base的原因,在于统一管理子类,在于简化子类的代码逻辑,便于提供公共的方法让子类实现或调用,BaseDialog这里继承的是DialogFragment,最大的原因就是,容易通过生命周期回调来管理弹窗,还有对于复杂样式的弹窗,使用DialogFragment会更加方便和高效


和之前封装Activity一样,做为一个抽象父类,子类要实现的无非就是,视图的传递和逻辑的处理,我们就可以在父类中进行定义抽象方法,Dialog一般有自己定义的样式,我们也可以定义一个初始化样式的方法。


/*** AUTHOR:AbnerMing* INTRODUCE:初始化数据*/abstractfuninitData()
/*** AUTHOR:AbnerMing* INTRODUCE:初始化样式*/abstractfuninitStyle()
/*** AUTHOR:AbnerMing* INTRODUCE:传递的视图*/abstractfungetLayoutId(): Int


除了必要实现的方法之外,我们还可以把一些公用的方法,定义到Base里,如获取View的方法,获取控件的方法等,这么做的目的,便于子类自定义实现一些效果以及减少findViewById的调用次数。


/*** AUTHOR:AbnerMing* INTRODUCE:获取View视图*/fun<V>findView(id: Int): View {
varview=mViewSparseArray[id]
if (view==null) {
view=mView?.findViewById(id)
mViewSparseArray.put(id, view)
        }
returnview    }
/*** AUTHOR:AbnerMing* INTRODUCE:获取当前View视图*/fungetDialogView(): View {
returnmView!!    }


以上只是列举了几个实现的方法,完整的代码,大家可以看源码中的BaseDialog类。


2、拓展ViewDataBinding形式Dialog


正常的普通Dialog就可以继承BaseDialog,基本就可以满足需要的,若是要和ViewDataBinding进行结合,那么就需要拓展需求了,具体的拓展也很简单,一是绑定View,二是绑定数据,完整的代码,大家可以看源码中BaseBindingDialog类。


绑定View


通过DataBindingUtil的bind方法,得到ViewDataBinding。


mBinding=DataBindingUtil.bind(getDialogView())


绑定数据


完成xml视图和数据的绑定。


mBinding.setVariable(variableId, t)
mBinding.executePendingBindings()


3、封装工具类,拓展相关功能


为了更加方便的让调用者使用,封装拓展函数是很有必要的,要不然,调用者每次都得要继承上边的两个父类,这样的代码就会增加很多,还会创建很多的类,我们需要单独的创建一个工具类,来实例化我们需要简化的功能逻辑。


提供添加xml视图的方法


很简单的一个普通方法,没什么好说的,把传递的xml,赋值给重写的getLayoutId方法即可。


/*** AUTHOR:AbnerMing* INTRODUCE:设置layout* @param mLayoutId xml布局*/funaddLayout(mLayoutId: Int): VipDialog {
this.mLayoutId=mLayoutIdreturnthis    }


提供普通使用和DataBinding形式使用方法


普通和DataBinding方法,这里用到了接口回调,接口的实现则在initVMData方法里,两个方法本身功能是一样的,无非就是一个是普通,一个是返回ViewDataBinding。


/*** AUTHOR:AbnerMing* INTRODUCE:初始化数据*/fun<VB : ViewDataBinding>bind(block: (bind: VB) ->Unit): VipDialog {
setDataCallBackListener(object : OnDialogDataCallbackListener {
overridefundataCallback() {
block.invoke(getDataBinding())
            }
        })
returnthis    }
/*** AUTHOR:AbnerMing* INTRODUCE:初始化数据*/funset(block: () ->Unit): VipDialog {
setDataCallBackListener(object : OnDialogDataCallbackListener {
overridefundataCallback() {
block.invoke()
            }
        })
returnthis    }


提供设置样式的方法


样式的设置也就是使用了接口回调。


/*** AUTHOR:AbnerMing* INTRODUCE:设置样式*/funstyle(style: () ->Unit): VipDialog {
setStyleCallBackListener(object : OnStyleCallBackListener {
overridefunstyleCallback() {
style.invoke()
            }
        })
returnthis    }


提供获取ViewDataBinding的方法


这个方法的提供是便于拿到ViewDataBinding,有效的更新视图数据。


/*** AUTHOR:AbnerMing* INTRODUCE:获取ViewDataBinding*/fun<VB : ViewDataBinding>getDataBinding(): VB {
returnmBindingasVB    }

 

我们看下整体的代码,如下:


/***AUTHOR:AbnerMing*DATE:2022/11/22*INTRODUCE:实例化功能*/classVipDialog : BaseBindingDialog<ViewDataBinding>() {
companionobject {
funinit(): VipDialog {
returnVipDialog()
        }
    }
privatevarmLayoutId=0overridefuninitVMData() {
mOnDialogDataCallbackListener?.dataCallback()
    }
overridefuninitStyle() {
mOnStyleCallBackListener?.styleCallback()
    }
overridefungetLayoutId(): Int {
returnmLayoutId    }
/*** AUTHOR:AbnerMing* INTRODUCE:获取ViewDataBinding*/fun<VB : ViewDataBinding>getDataBinding(): VB {
returnmBindingasVB    }
/*** AUTHOR:AbnerMing* INTRODUCE:设置layout* @param mLayoutId xml布局*/funaddLayout(mLayoutId: Int): VipDialog {
this.mLayoutId=mLayoutIdreturnthis    }
/*** AUTHOR:AbnerMing* INTRODUCE:初始化数据*/fun<VB : ViewDataBinding>bind(block: (bind: VB) ->Unit): VipDialog {
setDataCallBackListener(object : OnDialogDataCallbackListener {
overridefundataCallback() {
block.invoke(getDataBinding())
            }
        })
returnthis    }
/*** AUTHOR:AbnerMing* INTRODUCE:初始化数据*/funset(block: () ->Unit): VipDialog {
setDataCallBackListener(object : OnDialogDataCallbackListener {
overridefundataCallback() {
block.invoke()
            }
        })
returnthis    }
/*** AUTHOR:AbnerMing* INTRODUCE:设置样式*/funstyle(style: () ->Unit): VipDialog {
setStyleCallBackListener(object : OnStyleCallBackListener {
overridefunstyleCallback() {
style.invoke()
            }
        })
returnthis    }
privatevarmOnDialogDataCallbackListener: OnDialogDataCallbackListener?=nullprivatefunsetDataCallBackListener(mOnDialogDataCallbackListener: OnDialogDataCallbackListener) {
this.mOnDialogDataCallbackListener=mOnDialogDataCallbackListener    }
privatevarmOnStyleCallBackListener: OnStyleCallBackListener?=nullprivatefunsetStyleCallBackListener(mOnStyleCallBackListener: OnStyleCallBackListener) {
this.mOnStyleCallBackListener=mOnStyleCallBackListener    }
}


4、封装拓展函数,简化调用


dialog的弹出可能有很多场景,比如Activity里,比如Fragment里,比如一个工具类中,我们可以根据已知的场景,来定义我们的调用方式,目前,我定义了两种,在Activity或者Fragment里可以直接进行调用,也就是开头的调用方式,当然了,大家也可以自己拓展。


/*** AUTHOR:AbnerMing* INTRODUCE:Activity显示Dialog*/funAppCompatActivity.showVipDialog(vipDialog: VipDialog.() ->Unit): VipDialog {
valdialog=VipDialog.init()
dialog.apply(vipDialog)
setActivityDialog(this.supportFragmentManager, dialog)
returndialog}
/*** AUTHOR:AbnerMing* INTRODUCE:Fragment显示Dialog*/funFragment.showVipDialog(vipDialog: VipDialog.() ->Unit): VipDialog {
valdialog=VipDialog.init()
dialog.apply(vipDialog)
setActivityDialog(this.childFragmentManager, dialog)
returndialog}


通过以上几步,我们就可以实现开头的简单调用,具体的大家可以查看相关源码。


三、开源地址


项目地址:https://github.com/AbnerMing888/VipDialog


四、总结及注意事项


在开头已经阐述,这种方式易于拓展,但是代码量相对比较多,毕竟所有的UI和逻辑都必须独自来处理,在项目中的解决方式为,如果很多的弹框效果一样,建议再封装一层,抽取公共的工具类。


还有一个需要注意的,本身扩展函数showVipDialog返回的就是调用的类,也就是一个Dialog,大家可以直接获取变量,在其他的地方做更新Dialog或者销毁的操作。

valdialog=showVipDialog {
……}


相关文章
|
前端开发 Android开发 容器
surfaceview组件的surfaceCreated()不被调用的解决方案
原文:surfaceview组件的surfaceCreated()不被调用的解决方案 有时候我们有需要在native层做在surfaceview的上下文中做渲染,这个时候只是提供了一个单独什么都不做的surfaceview。
3542 0
|
3月前
|
API
[组件封装]API式调用-封装一个审核意见的组件Comments
[组件封装]API式调用-封装一个审核意见的组件Comments
20 0
构建一个可复用的自定义BaseAdapter
本节给大家带来的是构建一个可复用的自定义BaseAdapter,我们每每涉及到ListView GridView等其他的Adapter控件,都需要自己另外写一个BaseAdapter类,这样显得非常麻烦, 又比如,我们想在一个界面显示两个ListView的话,我们也是需要些两个BaseAdapter。
91 0
dialog快速实现,无需继承Dialog类,实现播放视频!
dialog快速实现,无需继承Dialog类,实现播放视频!
dialog快速实现,无需继承Dialog类,实现播放视频!
|
API
api和封装,让Modal组件使用更加便捷
api和封装,让Modal组件使用更加便捷
156 0
|
前端开发
前端学习案例-手动封装element-ui里面的button组件
前端学习案例-手动封装element-ui里面的button组件
116 0
|
数据可视化 定位技术 计算机视觉
qml开发笔记(三):可视化元素基类Item详解(下半场)
qml开发笔记(三):可视化元素基类Item详解(下半场)
qml开发笔记(三):可视化元素基类Item详解(下半场)
|
Java Android开发
【JetPack】为现有 Android 项目配置视图绑定 ( ViewBinding ) 模块 ( 视图绑定不影响传统布局操作 | 视图绑定类关联 Activity | 视图绑定类本质 )(一)
【JetPack】为现有 Android 项目配置视图绑定 ( ViewBinding ) 模块 ( 视图绑定不影响传统布局操作 | 视图绑定类关联 Activity | 视图绑定类本质 )(一)
204 0
【JetPack】为现有 Android 项目配置视图绑定 ( ViewBinding ) 模块 ( 视图绑定不影响传统布局操作 | 视图绑定类关联 Activity | 视图绑定类本质 )(一)
使用Unity获取所有子对象及拓展方法的使用
这个问题还是比较简单的,无非就是一个for循环就可以全部获取到了,但是我喜欢简单直达,有没有直接就能获取到所有的子对象函数呢,搜了好久都没有,所以我准备写一个扩展函数,来自己补充这个函数,一起来看一下吧。
|
Android开发
【JetPack】为现有 Android 项目配置视图绑定 ( ViewBinding ) 模块 ( 视图绑定不影响传统布局操作 | 视图绑定类关联 Activity | 视图绑定类本质 )(二)
【JetPack】为现有 Android 项目配置视图绑定 ( ViewBinding ) 模块 ( 视图绑定不影响传统布局操作 | 视图绑定类关联 Activity | 视图绑定类本质 )(二)
178 0