DSL(domain specific language),即领域专用语言:专门解决某一特定问题的计算机语言,比如大家耳熟能详的 SQL 和正则表达式。今天,我展示如何实现某种类型的DSL,我们将在Kotlin中包装一个现有的Java Builder。在java中我们经常会用到构建者模式,例如,如果您是Android开发人员,您一定使用了AlertDialog.Builder
,一个OkHttpClient.Builder
,或Retrofit.Builder
。与一般API不同的是,DSL提供了一种特殊的语法结构,其目的也就是为了让代码更易于理解。
在Kotlin的世界中,有诸多的语言特性可以用于构造更为简洁明晰的DSL:
先定义一个CustomDialogFragment
,实现了项目中规定的Dialog样式,并提供相应的接口用于设置Title,Message,LeftButton和RightButton。
val dialogFragment = CustomDialogFragment.newInstance()
dialogFragment.title = "title"
dialogFragment.message = "message"
dialogFragment.rightClicks(key = "确定", dismissAfterClick = true) {
toast("clicked!")
}
val ft = supportFragmentManager.beginTransaction()
val prev = supportFragmentManager.findFragmentByTag("dialog")
if (prev != null) {
ft.remove(prev)
}
ft.addToBackStack(null)
dialogFragment.show(ft, "dialog")
showDialog {
title = "title"
message = "message"
rightClicks {
toast("clicked!")
}
}
简洁性
可读性
inline fun AppCompatActivity.showDialog(settings: CustomDialogFragment.() -> Unit) : CustomDialogFragment {
val dialog = CustomDialogFragment.newInstance()
dialog.apply(settings)
val ft = this.supportFragmentManager.beginTransaction()
val prev = this.supportFragmentManager.findFragmentByTag("dialog")
if (prev != null) {
ft.remove(prev)
}
ft.addToBackStack(null)
dialog.show(ft, "dialog")
return dialog
}
AppCompatActivity
增加 showDialog( ) 扩展函数,这样就可以直接在Activity中调用showDialog( )方法展示Dialog了。
showDialog( )方法中只有唯一的参数settings,其类型是CustomDialogFragment.() -> Unit
,即带有CustomDialogFragment参数类型的函数。
在showDialog( )方法内部,构造了CustomDialogFragment对象,并调用dialog.apply(settings)
方法,其作用即在构造Dialog对象后,对Dialog进行设置。在实际调用showDialog( )方法时,就可以持有该CustomDialogFragment对象,然后调用CustomDialogFragment提供的public接口配置Dialog。
fun leftClicks(key: String = "取消", dismissAfterClick: Boolean = true, callback: () -> Unit) {
leftKey = key
leftButtonDismissAfterClick = dismissAfterClick
leftClicks = callback
}
在CustomDialogFragment提供的接口中,对一些变化不大的参数设置默认值,由于Kotlin语言支持默认参数,无需编写大量的重载方法。
最后提供两个警告类弹窗CustomAlertDialogFragment和确认取消弹窗CustomDialogFragment,有其他需要灵活定制
/**
*创建时间:2020/6/19
*编写人:kanghb
*功能描述:警告弹窗
*/
class CustomAlertDialogFragment : DialogFragment() {
private var tvAlert: TextView? = null
private var ivAlert: ImageView? = null
private var sureButton: Button? = null
private var sureClick: (() -> Unit)? = null
var cancelOutside: Boolean = true
var message: String? = null
var alsertSrc: Int = R.drawable.ic_toast_warn
var onDismissAfterClick = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setStyle(STYLE_NO_TITLE, android.R.style.Theme_Material_Light_Dialog)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.dialog_offline_login, container)
tvAlert = view?.findViewById(R.id.tv_alert)
ivAlert = view?.findViewById(R.id.iv_alert)
sureButton = view?.findViewById(R.id.btn_sure)
init()
return view
}
private fun init() {
dialog?.setCancelable(cancelOutside)
message?.let { text ->
tvAlert?.visibility = View.VISIBLE
tvAlert?.text = text
}
ivAlert?.setImageResource(alsertSrc)
sureClick?.let { onClick ->
sureButton?.setOnClickListener {
onClick()
if (onDismissAfterClick) {
dismissAllowingStateLoss()
}
}
}
}
fun sureClick(dismissAfterClick: Boolean = true, callback: () -> Unit) {
onDismissAfterClick = dismissAfterClick
sureClick = callback
}
companion object {
fun newInstance(): CustomAlertDialogFragment {
return CustomAlertDialogFragment()
}
}
}
inline fun AppCompatActivity.showAlertDialog(settings: CustomAlertDialogFragment.() -> Unit): CustomAlertDialogFragment {
val dialog = CustomAlertDialogFragment.newInstance()
dialog.apply(settings)
val ft = supportFragmentManager.beginTransaction()
val prev = supportFragmentManager.findFragmentByTag("dialog")
if (prev != null) {
ft.remove(prev)
}
ft.addToBackStack(null)
dialog.show(ft, "dialog")
return dialog
}
/**
*创建时间:2020/6/19
*编写人:kanghb
*功能描述:kotlin弹窗基础类
*/
class CustomDialogFragment : DialogFragment() {
private var titleTv: TextView? = null
private var messageTv: TextView? = null
private var leftButton: TextView? = null
private var rightButton: TextView? = null
private var leftClicks: (() -> Unit)? = null
private var rightClicks: (() -> Unit)? = null
var cancelOutside: Boolean = true
var title: String? = null
var message: String? = null
var leftKey: String? = null
var leftButtonDismissAfterClick = true
var rightKey: String? = null
var rightButtonDismissAfterClick = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setStyle(STYLE_NO_TITLE, android.R.style.Theme_Material_Light_Dialog)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.dialog_custom, container)
titleTv = view?.findViewById(R.id.title_tv)
messageTv = view?.findViewById(R.id.message_tv)
leftButton = view?.findViewById(R.id.left_button)
rightButton = view?.findViewById(R.id.right_button)
init()
return view
}
private fun init() {
dialog?.setCancelable(cancelOutside)
title?.let { text ->
titleTv?.visibility = View.VISIBLE
titleTv?.text = text
}
message?.let { text ->
messageTv?.visibility = View.VISIBLE
messageTv?.text = text
}
leftClicks?.let { onClick ->
leftButton?.text = leftKey
leftButton?.visibility = View.VISIBLE
leftButton?.setOnClickListener {
onClick()
if (leftButtonDismissAfterClick) {
dismissAllowingStateLoss()
}
}
}
rightClicks?.let { onClick ->
rightButton?.text = rightKey
rightButton?.setOnClickListener {
onClick()
if (rightButtonDismissAfterClick) {
dismissAllowingStateLoss()
}
}
}
}
fun leftClicks(key: String = "取消", dismissAfterClick: Boolean = true, callback: () -> Unit) {
leftKey = key
leftButtonDismissAfterClick = dismissAfterClick
leftClicks = callback
}
fun rightClicks(key: String = "确定", dismissAfterClick: Boolean = true, callback: () -> Unit) {
rightKey = key
rightButtonDismissAfterClick = dismissAfterClick
rightClicks = callback
}
companion object {
fun newInstance(): CustomDialogFragment {
return CustomDialogFragment()
}
}
}
inline fun AppCompatActivity.showDialog(settings: CustomDialogFragment.() -> Unit) : CustomDialogFragment {
val dialog = CustomDialogFragment.newInstance()
dialog.apply(settings)
val ft = supportFragmentManager.beginTransaction()
val prev = supportFragmentManager.findFragmentByTag("dialog")
if (prev != null) {
ft.remove(prev)
}
ft.addToBackStack(null)
dialog.show(ft, "dialog")
return dialog
}