前言
有一些时候,我们希望调用的方法不是立即返回值,而是希望它在和用户交互后才产生返回值。
当我们在安卓使用databinding时,我们可能会遇到一些这样的问题,拆分Activity和Model后虽然解决了臃肿问题,但是又出现了Model需要回调信息给Activity的问题,实际上我们在之前用Java也可以解决它,使用接口和匿名函数是可以完成的,但是现在换用了Kotlin,我们就可以用另一种方式了。
问题案例
问题概述

如图,我们有一个BottomSheetDialog,但它可能将在未来很多地方复用,为了方便,我封装了它。最终我将View,bottomSheetDialog,binding以及BottomSheetBehavior的处理做了抽离成了方法,如同下面。
但是这个对话框不仅仅如此,点击完成登录后,登录验证需要在Model完成,验证结果没问题后需要传值给Fragment,仔细一想为什么不直接在Fragment拿Model登录完成后储存的值呢?显然我们并不知道这个登录过程是否完成,因此,我们才需要解决一下这个问题,登录完成后传值通知Fragment登录流程结束。
OK,我们先看看简单的封装,如下。

如果是一个简单的对话框,显然它甚至不需要binding,自然也不需要返回,但是随着需求增加,像上面的那个扫码登陆对话框就得使用binding了,我并不想增加DialogUtils的代码量,因此,这样的需求我们就需要内联函数来解决了。
问题分析
这样我们的话需要一个类似Java接口的东西,像okhttp那样回调数据过来。实际上kotlin也有匿名函数
var responseResult: (Int, LoginStateBean) -> Unit = { code, loginStateBean ->
println(code.toString() + loginStateBean)
}
更重要的是他可以作为另一个函数的参数,这是很有意思的,相当于Java中的接口或者抽象方法,但我们不需要单独建立一个类,而是直接去使用一个匿名函数作为参数,直接在接受参数的方法内回调。
OK,我们使用这个似乎就可以解决刚刚的问题了,让我们试试看。

dialog_login_qr_bottomsheet.xml中使用了databinding,导入了两个实体类,LoginQrcodeBean的DataBean以及LoginQRModel。其中DataBean是请求登录二维码接口的返回数据类,LoginQRModel则是刷新二维码和完成登录回调方法的类。
同时他们由需要通过DialogUtils这个类来设置,因此,这个内联函数需要作为参数传给DialogUtils,由DialogUtils通过databinding再设置给LoginQRModel,同时也将binding传递过去,方便设置事件,当用户点击完成登录后,LoginQRModel的方法响应,并且在请求结束后回调给Fragment。
DialogUtils的登录对话框方法,我们注意到有一个参数是函数,也就是说这个是一个以函数为参数的函数。

LoginQRModel的匿名函数承接变量,与回调展示。

下面是Fragment的传值,当最后一个参数是函数时,可以写在外部。

代码实现
DialogUtils类
/**
* 全局使用弹窗工具类
*/
/**
* 全局使用弹窗工具类
*/
class DialogUtils {
private val TAG = DialogUtils::class.java.simpleName
/**
* 登陆对话框
* @param context Context
*/
@SuppressLint("InflateParams")
fun loginDialog(context: Context): BottomSheetDialog {
//先获取View实例
val view: View = LayoutInflater.from(context)
.inflate(R.layout.dialog_login_bottomsheet, null, false)
val bottomSheetDialog = initBottomSheetDialog(context, view)
//用户行为
val mDialogBehavior = initDialogBehavior(R.id.dialog_login_tip_bar, context, view)
//自定义方案
//mDialogBehavior.peekHeight = 600
return bottomSheetDialog
}
/**
* 本地/AS绑定 B站账号登陆弹窗
* @param activity Activity
* @param loginQrcodeBean LoginQrcodeBean
* @return BottomSheetDialog
*/
fun loginQRDialog(
activity: Activity,
loginQrcodeBean: LoginQrcodeBean,
responseResult: (Int, LoginStateBean) -> Unit
): BottomSheetDialog {
val binding: DialogLoginQrBottomsheetBinding =
DialogLoginQrBottomsheetBinding.inflate(LayoutInflater.from(activity))
val bottomSheetDialog = BottomSheetDialog(activity, R.style.BottomSheetDialog)
//设置布局
bottomSheetDialog.setContentView(binding.root)
binding.dataBean = loginQrcodeBean.data
binding.loginQRModel = LoginQRModel()
binding.loginQRModel?.responseResult = responseResult
binding.loginQRModel?.activity = activity
//传导binding过去
binding.loginQRModel?.binding = binding
//用户行为
val mDialogBehavior = initDialogBehaviorBinding(binding.dialogLoginQrTipBar, activity, binding.root.parent)
//自定义方案
//mDialogBehavior.peekHeight = 600
return bottomSheetDialog
}
@SuppressLint("InflateParams")
fun loadDialog(context: Context): BottomSheetDialog {
//先获取View实例
val view: View = LayoutInflater.from(context)
.inflate(R.layout.dialog_load_bottomsheet, null, false)
//设置布局背景
val bottomSheetDialog = initBottomSheetDialog(context, view)
//用户行为
val mDialogBehavior = initDialogBehavior(R.id.dialog_load_tip_bar, context, view)
//自定义方案
//mDialogBehavior.peekHeight = 600
return bottomSheetDialog
}
//TODO 常用方法封装
private fun initDialogBehaviorBinding(tipView: View, context: Context, viewGroup: ViewParent) {
//用户行为
val mDialogBehavior = BottomSheetBehavior.from(viewGroup as View)
mDialogBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
@SuppressLint("UseCompatLoadingForDrawables")
override fun onStateChanged(bottomSheet: View, newState: Int) {
//拖动监听
val linearLayout: View = tipView
when (newState) {
1 -> {
linearLayout.background = context.getDrawable(R.color.color_primary)
}
4 -> {
linearLayout.background =
context.getDrawable(R.color.color_primary_variant)
}
3 -> {
linearLayout.background =
context.getDrawable(R.color.color_primary_variant)
}
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
//状态改变监听
}
})
}
private fun initDialogBehavior(
barId: Int,
context: Context,
view: View
): BottomSheetBehavior<View> {
//用户行为
val mDialogBehavior = BottomSheetBehavior.from(view.parent as View)
mDialogBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
@SuppressLint("UseCompatLoadingForDrawables")
override fun onStateChanged(bottomSheet: View, newState: Int) {
//拖动监听
val linearLayout: View? = view.findViewById(barId)
when (newState) {
1 -> {
linearLayout?.background = context.getDrawable(R.color.color_primary)
}
4 -> {
linearLayout?.background =
context.getDrawable(R.color.color_primary_variant)
}
3 -> {
linearLayout?.background =
context.getDrawable(R.color.color_primary_variant)
}
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
//状态改变监听
}
})
return mDialogBehavior
}
private fun initBottomSheetDialog(context: Context, view: View): BottomSheetDialog {
val bottomSheetDialog = BottomSheetDialog(context, R.style.BottomSheetDialog)
//设置布局
bottomSheetDialog.setContentView(view)
return bottomSheetDialog
}
}
class Dialo
LoginQRModel类
class LoginQRModel {
var binding: DialogLoginQrBottomsheetBinding? = null
var activity: Activity? = null
lateinit var responseResult: (Int, LoginStateBean) -> Unit
/**
* 完成登录方法
* @param view View
* @param qrcode_key String
*/
fun finishLogin(view: View, qrcode_key: String) {
val bottomSheetDialog = activity?.let { DialogUtils().loadDialog(it) }
bottomSheetDialog?.show()
HttpUtils().get(
BilibiliApi().getLoginStatePath + "?qrcode_key=" + qrcode_key,
object : Callback {
override fun onFailure(call: Call, e: IOException) {
bottomSheetDialog?.cancel()
}
@SuppressLint("CommitPrefEdits")
override fun onResponse(call: Call, response: Response) {
val loginStateBean: LoginStateBean =
Gson().fromJson(response.body?.string(), LoginStateBean::class.java)
//回调
bottomSheetDialog?.cancel()
//更新UI线程
activity?.runOnUiThread {
//启动sharedPreferences
val sharedPreferences: SharedPreferences =
activity!!.getSharedPreferences("data", MODE_PRIVATE)
val editor = sharedPreferences.edit()
responseResult(200, loginStateBean)
}
}
}
)
}
/**
* 重新加载二维码视图
* @param view View
* @param loginQrcodeDataBean DataBean
*/
fun reloadLoginQR(view: View, loginQrcodeDataBean: LoginQrcodeBean.DataBean) {
HttpUtils().get(
BilibiliApi().getLoginQRPath,
object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
val loginQrcodeBean =
Gson().fromJson(response.body?.string(), LoginQrcodeBean::class.java)
loginQrcodeDataBean.url = loginQrcodeBean.data.url
loginQrcodeDataBean.qrcode_key = loginQrcodeBean.data.qrcode_key
binding?.dataBean = loginQrcodeDataBean
}
}
)
}
}
文末
很高兴你看到了这里,如果发现上面有讲的不对,或者你有更好的办法,欢迎在评论区指出哦!!!
写下这篇文章,以免时间一长忘掉了。

发表回复