Kotlin之编写工具方法

Kotlin提供的丰富的语法特性给我们提供了无限扩展的可能,各种复杂的API经过特定的封装处理之后都能变得简单易用。

1.求N个数的最大最小值

两个数比大小这个功能,如果我想要获取两个数中较大的那个数,除了使用最基本的if语句之外,还可以借助Kotlin内置的max函数,如下所示:

val a=10
val b=15
val larger=kotlin.math.max(a,b)

当我们想要在3个数中获取最大的那个数,应该怎么写?由于max()函数只能接收两个参数,因此需要先比较前两个数的大小,然后再拿较大的那个数和剩余的数比较,写法如下:

val a=10
val b=15
val c=5
val larger=kotlin.math.max(max(a,b),c)

3个数中获取最大值就需要使用这种嵌套max()函数的写法了,那如果是4个数、5个数呢?这个时候我们可以对max()函数的用法进行简化。
Kotlin中的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
    
}

这里max()函数的参数声明中使用了vararg关键字,也就是说现在它可以接收任意多个整型参数。接着我们使用了一个maxNum变量来记录所有数的最大值,并在一开始将他赋值成了整型的最小值。然后使用for-in循环遍历nums参数列表,如果发现当前遍历的数字比maxNum更大,就将maxNum的值更新成这个数,最终将maxNum返回即可。
这样经过一层的封装之后,我们在使用max()函数就会变得更简便,比如:

val a=10
val b=15
val c=5
val larger=max(a,b,c)

这样就可以摆脱嵌套函数调用的写法,不管是求3个数的最大值还是求N个数的最大值,只需要不断地给max()函数传入参数就可以了。
但是现在自定义的max()函数有个缺点,就是它只能求N个整型数字的最大值,如果我还想要求N个浮点型或长整型数字的最大值。其中一个方法就是对max()函数进行重载,来接收不同类型的参数,但是这中方案实现起来比较繁琐,而且有大量的重复代码。我们可以泛型,将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 >的子类型了。接下来,我们判断nums参数列表是否为空,如果为空的话就主动抛出一个异常,提醒调用者max()函数必须传入参数,紧接着将maxNum的值赋值成nums参数列表中第一个参数的值,然后同样是遍历参数列表,如果发现了更大的值就对maxNum进行更新。
经过这样修改之后,我们可以灵活地使用max()函数,比如说求3个浮点型数字的最大值

fun main(){
    val a=3.5
    val b=3.8
    val c=4.1
    val larger= max(a,b,c)
    println(larger)
}

2.简化Toast的用法

首先回顾一下Toast的标准用法,如果想要在界面上弹出一段文字提示需要这样写:

Toast.makeText(context,"This is Toast",Toast.LENGTH_SHORT).show()

Toast的makeText()方法接收3个参数:第一个参数是Toast显示的上下文环境;第二个参数是Toast显示的内容,需要由调用方进行指定,可以传入字符串和字符串资源id两种类型;第三个参数是Toast显示的时长,只支持Toast.LENGTH_SHORT和Toast.LENGTH_LONG这两种值。
那么我们可以String类和Int类各添加一个扩展函数,并在里面封装弹出Toast的具体逻辑。这样以后每次想要弹出Toast提示时,只需要调用它们的扩展函数就可以了。

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()
}

这里分别给String类和Int类新增一个showToast()函数,并让它们都接收一个Context参数。然后在函数的内部,我们仍然使用了Toast的原生API用法,只是将弹出内容改为this,另外将Toast的显示时长固定设置成Toast.LENGTH_SHORT。
现在同样是弹出一段文字提醒就可以这么写:

"This is Toast".showToast(context)

如果想弹出一个定义在strings.xml中的字符串资源,写法如下:

R.String.app_name.showToast(context)

这两种写法分别调用的就是我们刚才的String类和Int类中添加的showToast()扩展函数。
还可以对Toast的显示时长进行动态指定,我们给函数设定默认值功能,这样就可以在不增加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()
}

我们给showToast()函数增加一个显示时长参数,但同时也给它指定了一个默认值。这样我们之前所使用的showToast()函数的写法将完全不受影响,默认会使用Toast.LENGTH_SHORT类型的显示时长。如果想使用Toast.LENGTH_LONG的显示时长可以这样写:

  "This is Toast".showToast(context,Toast.LENGTH_LONG)

3.简化Snackbar的用法

Snackbar控件和Toast的用法基本相似,但是又比Toast稍微复杂一些

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

可以看到Snackbar中make()方法的第一个参数是View,而Toast中makeText()方法的第一个参数是Context,另外Snackbar还可以调用setAction()方法来设置一个额外的点击事件。
由于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类当中,并在参数列表上声明了Snackbar要显示的内容以及显示的时长。
想要使用Snackbar显示一段文本提示,只需要这样写就可以了:

view.showSnackbar("This is Snackbar")

我们还可以让showSnackbar()函数再额外接收一个函数类型参数,以此来实现Snackbar的完整功能。

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()
}

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

  view.showSnackbar("This is Snackbar","Action"){
            //处理具体逻辑            
        }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值