Kotlin DSL回调

缘起【熊猫先生】的文章【如何让你的回调更具Kotlin风味】

1. 个人关于回调的理解

  • 无非就是【持有对象】,然后触发,Kotlin & Java 别无二致
  • Kotlin 好处就是可不必定义接口,用 Lambda 传递触发即可

2. 以杜甫先生的高作【登高】弹窗示例

20220606170913.jpg

xml 文件 dialog_test.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/shape_dncard_top_14dp"
    android:gravity="bottom"
    android:orientation="vertical"
    tools:ignore="HardcodedText">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="55dp"
        android:gravity="center"
        android:text="登高"
        android:textColor="@color/cl_333333"
        android:textSize="17sp"
        android:textStyle="bold" />

    <View
        android:layout_width="match_parent"
        android:layout_height="7dp"
        android:background="@color/dn_page_bg" />

    <androidx.appcompat.widget.AppCompatTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:lineSpacingMultiplier="1.2"
        android:padding="10dp"
        android:text="@string/denggao"
        android:textColor="@color/cl_333333"
        android:textSize="14sp" />

    <View
        android:id="@+id/divider"
        android:layout_width="match_parent"
        android:layout_height="7dp"
        android:background="@color/dn_page_bg" />

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="55dp"
        android:orientation="horizontal">

        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/tvCancel"
            android:layout_width="0dp"
            android:layout_height="55dp"
            android:layout_weight="1"
            android:background="@drawable/sel_white"
            android:gravity="center"
            android:text="取消"
            android:textColor="@color/cl_333333"
            android:textSize="@dimen/sp_15"
            android:textStyle="bold" />

        <View
            android:layout_width="2dp"
            android:layout_height="match_parent"
            android:background="@color/split_line" />

        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/tvConfirm"
            android:layout_width="0dp"
            android:layout_height="@dimen/dp_55"
            android:layout_weight="1"
            android:background="@drawable/sel_white"
            android:gravity="center"
            android:text="@string/confirm"
            android:textColor="@color/cl_333333"
            android:textSize="@dimen/sp_15"
            android:textStyle="bold" />

    </androidx.appcompat.widget.LinearLayoutCompat>


</LinearLayout>

TestDialog.kt 文件


import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.Gravity
import android.view.View
import androidx.appcompat.widget.AppCompatTextView
import com.jhj.cloudman.R
import com.jhj.cloudman.utils.phonedevice.getScreenWidth

/**
 * @author 刘建波
 * 时间:2022/6/6
 * 描述:杜甫【登高】,弹窗
 */
class TestDialog(context: Context) : Dialog(context, R.style.SimpleDialogStyle) {

    /**
     * 定义两个 lambda 回调
     */
    private var mConfirmCallback: (() -> Unit)? = null
    private var mCancelCallback: (() -> Unit)? = null


    @SuppressLint("InflateParams")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val view = layoutInflater.inflate(R.layout.dialog_test, null)
        initView(view)
        setContentView(view)
        val window = window
        if (window != null) {
            val layoutParams = window.attributes
            layoutParams.gravity = Gravity.BOTTOM
            layoutParams.width = (context.getScreenWidth() * 1.0f).toInt()
            window.attributes = layoutParams
        }
    }

    /**
     * 初始化页面 UI 以及注册/处理事件
     */
    private fun initView(view: View) {
        val tvCancel = view.findViewById<AppCompatTextView>(R.id.tvCancel)
        tvCancel.setOnClickListener {
            mCancelCallback?.invoke()
            dismiss()
        }
        val tvConfirm = view.findViewById<AppCompatTextView>(R.id.tvConfirm)
        tvConfirm.setOnClickListener {
            mConfirmCallback?.invoke()
            dismiss()
        }
    }

    /**
     * 传递 Confirm Lambda 回调
     */
    fun confirmCallback(action: () -> Unit): TestDialog {
        this.mConfirmCallback = action
        return this
    }

    /**
     * 传递 Cancel Lambda 回调
     */
    fun cancelCallback(action: () -> Unit): TestDialog {
        this.mCancelCallback = action
        return this
    }

    /**
     * 设置点击返回按钮,是否可以隐藏弹窗(默认为可隐藏)
     */
    fun cancelable(cancelable: Boolean): TestDialog {
        setCancelable(cancelable)
        return this
    }

    /**
     * 设置点击弹窗外部,是否可以隐藏弹窗(默认为可隐藏)
     */
    fun canceledOnTouchOutside(cancelable: Boolean): TestDialog {
        setCanceledOnTouchOutside(cancelable)
        return this
    }

}

测试代码

    TestDialog(_mActivity)
    .confirmCallback {
        Log.d("liujianbo", "点击了【确定】")
    }
    .cancelCallback {
        Log.d("liujianbo", "点击了【取消】")
    }
    .show()

测试结果

2022-06-06 17:17:46.144 17882-17882/com.jhj.cloudman D/liujianbo: 点击了【取消】
2022-06-06 17:17:49.618 17882-17882/com.jhj.cloudman D/liujianbo: 点击了【确定】

到这里完全没什么好说的

  • 1 定义 lambda 形式的回调
  • 2. 传递 lambda 形式的回调
  • 3. 触发回调

如果还有什么要说的,无非就是一个 return this 的链式调用
走到这里,朴素的代码我已经觉得挺好的【代码朴素,性能也好】,DSL 觉得这没有 Kotlin 的味道,或者说【不够高级,看不出我们比 Java 优雅】

DSL让回调包一层,大回调里套小回调,接下来我们修改文件

  1. xml 文件不变
  2. TestDialog.kt 引入【内部类】作为外层的大回调
package com.jhj.cloudman.common.dialog.publish

import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.Gravity
import android.view.View
import androidx.appcompat.widget.AppCompatTextView
import com.jhj.cloudman.R
import com.jhj.cloudman.utils.phonedevice.getScreenWidth

/**
 * @author 刘建波
 * 时间:2022/6/6
 * 描述:杜甫【登高】,弹窗
 */
class TestDialog(context: Context) : Dialog(context, R.style.SimpleDialogStyle) {

    /**
     * 定义内部类
     */
    inner class ListenerBuilder {
        /**
         * 把 lambda 回调,放入内部类中
         */
        internal var mConfirmCallback: (() -> Unit)? = null
        internal var mCancelCallback: (() -> Unit)? = null

        /**
         * 传递 Confirm Lambda 回调
         */
        fun confirmCallback(action: () -> Unit) {
            this.mConfirmCallback = action
        }

        /**
         * 传递 Cancel Lambda 回调
         */
        fun cancelCallback(action: () -> Unit) {
            this.mCancelCallback = action
        }

    }

    private var mListener: ListenerBuilder? = null

    /**
     * 初始化[mListener],并用"带接受者的 Lambda 传递 lambda 回调"
     */
    fun listener(listenerBuilder: ListenerBuilder.() -> Unit): TestDialog {
        mListener = ListenerBuilder().apply(listenerBuilder)
        return this
    }


    @SuppressLint("InflateParams")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val view = layoutInflater.inflate(R.layout.dialog_test, null)
        initView(view)
        setContentView(view)
        val window = window
        if (window != null) {
            val layoutParams = window.attributes
            layoutParams.gravity = Gravity.BOTTOM
            layoutParams.width = (context.getScreenWidth() * 1.0f).toInt()
            window.attributes = layoutParams
        }
    }

    /**
     * 初始化页面 UI 以及注册/处理事件
     */
    private fun initView(view: View) {
        val tvCancel = view.findViewById<AppCompatTextView>(R.id.tvCancel)
        tvCancel.setOnClickListener {
//          mCancelCallback?.invoke()
            mListener?.mCancelCallback?.invoke() // invoke 也包一层
            dismiss()
        }
        val tvConfirm = view.findViewById<AppCompatTextView>(R.id.tvConfirm)
        tvConfirm.setOnClickListener {
            //mConfirmCallback?.invoke()
            mListener?.mConfirmCallback?.invoke() // invoke 也包一层
            dismiss()
        }
    }


    /**
     * 设置点击返回按钮,是否可以隐藏弹窗(默认为可隐藏)
     */
    fun cancelable(cancelable: Boolean): TestDialog {
        setCancelable(cancelable)
        return this
    }

    /**
     * 设置点击弹窗外部,是否可以隐藏弹窗(默认为可隐藏)
     */
    fun canceledOnTouchOutside(cancelable: Boolean): TestDialog {
        setCanceledOnTouchOutside(cancelable)
        return this
    }

}

测试代码【都不用深究 lambda 的本质,就套一层,都能算出性能更差】【但能装逼,哈哈哈】

    TestDialog(_mActivity)
    .listener {
        //带接受者的 lambda 内部可直接覆写方法
        confirmCallback {
            Log.d("liujianbo", "点击了【确定】")
        }
        cancelCallback {
            Log.d("liujianbo", "点击了【取消】")
        }
    }
    .show()

性能方面【熊猫先生】的文章【如何让你的回调更具Kotlin风味】说得清楚

写在最后的话
DSL 虽然,封装更麻烦,性能也差。但二选一的话,我还是选 DSL 回调,因为调用的时候,Java 开发就看不懂了,可以装逼。
学习如果不是为了装逼,那还不如去打游戏。溜了,嗯...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容