Kotlin DSL回調

緣起【熊貓先生】的文章【如何讓你的回調更具Kotlin風味】

1. 個人關于回調的理解

  • 無非就是【持有對象】,然后觸發(fā),Kotlin & Java 別無二致
  • Kotlin 好處就是可不必定義接口,用 Lambda 傳遞觸發(fā)即可

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. 觸發(fā)回調

如果還有什么要說的,無非就是一個 return this 的鏈式調用
走到這里,樸素的代碼我已經覺得挺好的【代碼樸素,性能也好】,DSL 覺得這沒有 Kotlin 的味道,或者說【不夠高級,看不出我們比 Java 優(yōu)雅】

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 開發(fā)就看不懂了,可以裝逼。
學習如果不是為了裝逼,那還不如去打游戲。溜了,嗯...

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容