imcys.com
遵从中二的召唤,来吧少年!

【Kotlin】利用内联函数实现回调

前言

有一些时候,我们希望调用的方法不是立即返回值,而是希望它在和用户交互后才产生返回值。

当我们在安卓使用databinding时,我们可能会遇到一些这样的问题,拆分ActivityModel后虽然解决了臃肿问题,但是又出现了Model需要回调信息给Activity的问题,实际上我们在之前用Java也可以解决它,使用接口和匿名函数是可以完成的,但是现在换用了Kotlin,我们就可以用另一种方式了。

问题案例

问题概述

如图,我们有一个BottomSheetDialog,但它可能将在未来很多地方复用,为了方便,我封装了它。最终我将View,bottomSheetDialog,binding以及BottomSheetBehavior的处理做了抽离成了方法,如同下面。

但是这个对话框不仅仅如此,点击完成登录后,登录验证需要在Model完成,验证结果没问题后需要传值给Fragment,仔细一想为什么不直接在FragmentModel登录完成后储存的值呢?显然我们并不知道这个登录过程是否完成,因此,我们才需要解决一下这个问题,登录完成后传值通知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,导入了两个实体类,LoginQrcodeBeanDataBean以及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
                }

            }
        )

    }

}

文末

很高兴你看到了这里,如果发现上面有讲的不对,或者你有更好的办法,欢迎在评论区指出哦!!!

写下这篇文章,以免时间一长忘掉了。

萌新杰少

文章作者

I im CYS,一个热爱二次元的大专开发者

发表回复

textsms
account_circle
email

Captcha Code

萌新杰少の秘密基地

【Kotlin】利用内联函数实现回调
有一些时候,我们希望调用的方法不是立即返回值,而是希望它在和用户交互后才产生返回值。
扫描二维码继续阅读
2022-10-28