kotlin特性自定义DialogFragment

介绍

DSL(domain specific language),即领域专用语言:专门解决某一特定问题的计算机语言,比如大家耳熟能详的 SQL 和正则表达式。今天,我展示如何实现某种类型的DSL,我们将在Kotlin中包装一个现有的Java Builder。在java中我们经常会用到构建者模式,例如,如果您是Android开发人员,您一定使用了AlertDialog.Builder,一个OkHttpClient.Builder,或Retrofit.Builder。与一般API不同的是,DSL提供了一种特殊的语法结构,其目的也就是为了让代码更易于理解。

在Kotlin的世界中,有诸多的语言特性可以用于构造更为简洁明晰的DSL:

  • 扩展函数
  • 默认参数
  • 中缀表达式
  • Lambda表达式
  • 函数参数
  • 带接收者的函数参数

DSL简化Dialog样板代码

先定义一个CustomDialogFragment,实现了项目中规定的Dialog样式,并提供相应的接口用于设置Title,Message,LeftButton和RightButton。

使用DSL前

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")

使用DSL后

showDialog {
    title = "title"
    message = "message"
    rightClicks {
        toast("clicked!")
    }
}
  1. 省略了大量样板代码,保证了代码的简洁性
  2. 语义清晰,具有极强的可读性
  3. 语法结构与一般API不同
  4. 应用于Android项目中的Dialog展示

如何实现?

1. 扩展函数

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了。

2. 带接收者的函数参数

showDialog( )方法中只有唯一的参数settings,其类型是CustomDialogFragment.() -> Unit,即带有CustomDialogFragment参数类型的函数。

在showDialog( )方法内部,构造了CustomDialogFragment对象,并调用dialog.apply(settings)方法,其作用即在构造Dialog对象后,对Dialog进行设置。在实际调用showDialog( )方法时,就可以持有该CustomDialogFragment对象,然后调用CustomDialogFragment提供的public接口配置Dialog。

3. 默认参数

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
}

你可能感兴趣的:(kotlin,安卓开发)