学习Kotlin之编写好用的工具

1 求N个数的最大最小值

  我们会想到用max()函数,但是它只能接受两个参数,如果需要比较的数量比较多,写起来就麻烦了。

  这个时候我们就可以使用vararg关键字,它允许方法接收多个同类型的参数。

  于是,新建一个Max.kt文件,自定义一个max()函数:

fun max(vararg nums: Int): Int {
    var maxNum = Int.MIN_VALUE
    for (num in nums) {
        maxNum = kotlin.math.max(maxNum,num)
    }
    return maxNum
}

  写法就如下所示:

    val a = 10
    val b = 20
    val c = 30
    val largest = max(a,b,c)

 

  不过目前这个max()函数还有一个缺点,就是它只能求N个整型数字的最大值,如果想要求N个浮点型或者长整型数字的最大值,该怎么办呢?这里就有一种巧妙的做法。

  Java中规定,所有类型的数字都是可比较的,因此必须实现Comparable接口,这个规定在Kotlin中也成立。

  那么我们就可以借助泛型,将max()函数修改为可以接收任意多个实现Comparable接口的参数:

fun <T : Comparable<T>> max(vararg nums: T): T {
    if (nums.isEmpty()) throw RuntimeException("Params can not be empty.")
    var maxNum = nums[0]
    for (num in nums) {
        if (num > maxNum) maxNum = num
    }
    return maxNum
}

  这里将泛型T的上界指定成了Comparable<T>,那么参数T必定就是Comparable<T>的子类型了。接着同样遍历寻找最大值。

  现在不管是双精度浮点型、单精度浮点型,还是其他类型的,只要是实现了Comparable接口的子类型,max()函数都支持获取最大值。

 

2 简化Toast的用法

  Toast的标准写法如下:

        Toast.makeText(context, "content", Toast.LENGTH_SHORT).show()

  每次使用的时候都要写这么一长串的代码,确实麻烦。

  我们可以在String类和Int类各添加一个扩展函数,并在里面封装Toast的使用方法。这样以后使用的时候就调用它们的扩展函数即可。

  新建一个Toast.kt文件:

fun String.showToast(context: Context) {
    Toast.makeText(context, this, Toast.LENGTH_SHORT).show()
}

fun Int.showToast(context: Context) {
    Toast.makeText(context, this, Toast.LENGTH_SHORT).show()
}

  经过这样的扩展之后,使用方法就变简单了:

        "This is a Toast.".showToast(context)

 

  当然,你也可以弹出一个定义在strings.xml中的字符串资源:

        R.string.app_name.showToast(context)

 

  如果我们需要手动传入显示时长参数的话,可以使用参数默认值的功能,就可以在不增加showToast()函数的使用复杂度的情况下,还能动态支持设置显示时长的功能。如下:

fun String.showToast(context: Context, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(context, this, duration).show()
}

fun Int.showToast(context: Context, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(context, this, duration).show()
}

  这样就可以手动指定时长,不写就用默认值。

  

3 简化Snackbar的用法

  Snackbar的常规用法:

        Snackbar.make(view, "this is a Snackbar.", Snackbar.LENGTH_SHORT)
            .setAction("Action"){
                //  处理具体逻辑
            }
            .show()

  由于make()方法接收一个View参数,Snackbar会使用这个View自动查找最外层布局展示Snackbar。因此我们可以给View类添加一个扩展函数,并在里面封装Snackbar的逻辑。新建一个Snackbar.kt文件:

fun View.showSnackbar(text: String, duration: Int = Snackbar.LENGTH_SHORT) {
    Snackbar.make(this, text, duration).show()
}

fun View.showSnackbar(resId: Int, duration: Int = Snackbar.LENGTH_SHORT) {
    Snackbar.make(this, resId, duration).show()
}

 

  这段代码跟上一节代码相似,只不过这里是将扩展函数添加到了View类中。这里也是支持传入字符串和字符串资源id的。

  使用方式:

        view.showSnackbar("this is a snackbar.")

 

  但是这里缺少了setAction()方法,这时候就要用上高阶函数了,我们让showSnackbar()函数再接收一个函数类型参数:

fun View.showSnackbar(text: String, actionText: String? = null, 
                      duration: Int = Snackbar.LENGTH_SHORT, block: (() -> Unit)? = null) {
    val snackbar = Snackbar.make(this, text, duration)
    if (actionText != null && block != null){
        snackbar.setAction(actionText){
            block()
        }
    }
    snackbar.show()
}

fun View.showSnackbar(resId: Int, actionResId: Int? = null, 
                      duration: Int = Snackbar.LENGTH_SHORT, block: (() -> Unit)? = null) {
    val snackbar = Snackbar.make(this, resId, duration)
    if (actionResId != null && block != null){
        snackbar.setAction(actionResId){
            block()
        }
    }
    snackbar.show()
}

  这里添加了函数类型参数,还新增了一个用于传递给setAction()方法的字符串或字符串资源id。将这连个参数都设置成可为空类型,并将默认值设置成空,然后只有当这两个参数都不为空时,才调用setAction()方法设置额外的点击事件。如果触发了点击事件,只需要调用函数类型参数将事件传递给外部的Lambda表达式即可。

  此时,showSnackbar()函数就比较完整了。写法如下:

        view.showSnackbar("this is a snackbar.", "Action") {
            //  处理逻辑
        }

 

  

  本次学习中,分别应用了顶层函数、扩展函数和高阶函数的知识,还有vararg、参数默认值等技巧。

 

 

 

 

 

 

 

 

 

  

posted @ 2022-03-17 17:50  PeacefulGemini  阅读(250)  评论(0)    收藏  举报
回顶部