緣起【熊貓先生】的文章【如何讓你的回調更具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讓回調包一層,大回調里套小回調,接下來我們修改文件
- xml 文件不變
- 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ā)就看不懂了,可以裝逼。
學習如果不是為了裝逼,那還不如去打游戲。溜了,嗯...