缘起【熊猫先生】的文章【如何让你的回调更具Kotlin风味】
1. 个人关于回调的理解
- 无非就是【持有对象】,然后触发,Kotlin & Java 别无二致
- Kotlin 好处就是可不必定义接口,用 Lambda 传递触发即可
2. 以杜甫先生的高作【登高】弹窗示例
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让回调包一层,大回调里套小回调,接下来我们修改文件
- 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 开发就看不懂了,可以装逼。
学习如果不是为了装逼,那还不如去打游戏。溜了,嗯...