前言
有一些时候,我们希望调用的方法不是立即返回值,而是希望它在和用户交互后才产生返回值。
当我们在安卓使用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 } } ) } }
文末
很高兴你看到了这里,如果发现上面有讲的不对,或者你有更好的办法,欢迎在评论区指出哦!!!
写下这篇文章,以免时间一长忘掉了。
发表回复