前言

家喻户晓,kotlin是google力推的用以取代java的android开发语言
kotlin应用起来比拟不便,同时有许多语法糖
本文次要解说了一些比拟实用的kotlin技巧

自定义圆角矩形

在我的项目中,咱们经常要定义圆角矩形背景,个别是用自定义drawable实现的
然而圆角矩形的背景与圆角经常会有轻微的变动,而一旦变动咱们又要新创建一个drawable文件
这样就会导致文件爆炸的问题

咱们能够利用kotlin的扩大函数,来实现简略不便的圆角矩形背景

fun View.setRoundRectBg(color: Int = Color.WHITE, cornerRadius: Int = 15.dp) {    background = GradientDrawable().apply {        setColor(color)        setCornerRadius(cornerRadius.toFloat())    }}

对于须要自定义背景的View,间接调用setRoundRectBg即可,简略不便

reified应用

reified,kotlin中的泛型实化关键字,使形象的货色更加具体或实在。
咱们举两个例子来看看怎么应用reified

startActivity例子

咱们个别startActivity是这样写的

startActivity(context, NewActivity::class.java)  

咱们利用reified定义一个扩大函数

// Functioninline fun <reified T : Activity> Activity.startActivity(context: Context) {    startActivity(Intent(context, T::class.java))}// CallerstartActivity<NewActivity>(context)

应用 reified,通过增加类型传递简化泛型参数
这样就不必手动传泛型的类型过来了

Gson解析例子

咱们首先看下个别咱们应用gson解析json是怎么做的
在Java序列化库(如Gson)中,当您想要反序列化该JSON字符串时,您最终必须将Class对象作为参数传递,以便Gson晓得您想要的类型。

User user = new Gson().fromJson(getJson(), User.class)

当初,让咱们一起展现reified类型实化参数的魔法 咱们将创立一个十分轻量级的扩大函数来包装Gson办法:

inline fun <reified T> Gson.fromJson(json: String) =         fromJson(json, T::class.java)

当初,在咱们的Kotlin代码中,咱们能够反序列化JSON字符串,甚至基本不须要传递类型信息!

val user: User = Gson().fromJson(json)

Kotlin依据它的用法推断出类型 - 因为咱们将它调配给User类型的变量,Kotlin应用它作为fromJson()的类型参数

kotin接口反对SAM转换

什么是SAM转换?可能有的同学还不太理解,这里先科普一下:

SAM 转换,即 Single Abstract Method Conversions,就是对于只有单个非默认形象办法接口的转换 —— 对于合乎这个条件的接口(称之为 SAM Type ),在 Kotlin 中能够间接用 Lambda 来示意 —— 当然前提是 Lambda 的所示意函数类型可能跟接口的中办法相匹配。

在Kotlin1.4之前,Kotlin是不反对Kotlin的SAM转换的,只反对Java SAM转换,官网给出的的解释是:是 Kotlin 自身曾经有了函数类型和高阶函数,不须要在去SAM转化。 这个解释开发者并不买账,如果你用过Java Lambda和Fuction Interface。当你切换到Kotlin时,就会很懵逼。看来Kotlin是意识到了这个,或者是看到开发者的反馈,终于反对了。

在1.4之前,只能传递一个对象,是不反对Kotlin SAM的,而在1.4之后,能够反对Kotlin SAM,然而用法有一丢丢变动。interface须要应用fun关键字申明。应用fun关键字标记接口后,只有将此类接口作为参数,就能够将lambda作为参数传递。

// 留神需用fun 关键字申明fun interface Action {    fun run()}fun runAction(a: Action) = a.run()fun main(){    // 1.4之前,只能应用object    runAction(object : Action{        override fun run() {            println("run action")        }    })     // 1.4-M1反对SAM,OK    runAction {        println("Hello, Kotlin 1.4!")    }}

委托

有时候,实现一些工作的办法是将它们委托给他人。这里不是在建议您将本人的工作委托给敌人去做,而是在说将一个对象的工作委托给另一个对象。

当然,委托在软件行业不是什么陈腐名词。委托 (Delegation) 是一种设计模式,在该模式中,对象会委托一个助手 (helper) 对象来解决申请,这个助手对象被称为代理。代理负责代表原始对象解决申请,并使后果可用于原始对象。

类委托

举个例子,当咱们要实现一个增强版的ArrayList,反对复原最初一次删除的item

实现这个用例的一种形式,是继承 ArrayList 类。因为新的类继承了具体的 ArrayList 类而不是实现 MutableList 接口,因而它与 ArrayList 的实现高度耦合。
如果只须要笼罩 remove() 函数来放弃对已删除我的项目的援用,并将 MutableList 的其余空实现委托给其余对象,那该有多好啊。为了实现这一指标,Kotlin 提供了一种将大部分工作委托给一个外部 ArrayList 实例并且能够自定义其行为的形式,并为此引入了一个新的关键字: by。

<!-- Copyright 2019 Google LLC.SPDX-License-Identifier: Apache-2.0 -->class ListWithTrash <T>(private val innerList: MutableList<T> = ArrayList<T>()) : MutableCollection<T> by innerList {    var deletedItem : T? = null    override fun remove(element: T): Boolean {           deletedItem = element            return innerList.remove(element)    }    fun recover(): T? {        return deletedItem    }}

by 关键字通知 Kotlin 将 MutableList 接口的性能委托给一个名为 innerList 的外部 ArrayList。通过桥接到外部 ArrayList 对象办法的形式,ListWithTrash 依然反对 MutableList 接口中的所有函数。与此同时,当初您能够增加本人的行为了。

属性委托

除了类代理,您还能够应用 by 关键字进行属性代理。通过应用属性代理,代理类会负责解决对应属性 get 与 set 函数的调用。这一个性在您须要在其余对象间复用 getter/setter 逻辑时非常有用,同时也能让您能够轻松地对简略反对字段的性能进行扩大

举个例子,利用委托属性能够封装SharedPreference
将数据存储操作委托给代理类有几个益处
1.则精简了代码,不便了存储与读取调用
2.与SP进行理解耦,后续如果要替换存储库,只须要批改代理类即可

调用如下:

object Pref: PreferenceHolder() {    var isFirstInstall: Boolean by bindToPreferenceField(false)    var time: Long? by bindToPreferenceFieldNullable()}

带状态的LiveData

目前咱们在开发的过程中越来越多的应用MVVM模式与ViewModel
咱们也经常用LiveData来标识网络申请状态
咱们须要定义申请开始,申请胜利,申请失败,三个LiveData

这其实也是很冗余反复的代码,因而咱们能够进行肯定的封装,封装一个带状态的LiveData

定义如下:

typealias StatefulLiveData<T> = LiveData<RequestState<T>>typealias StatefulMutableLiveData<T> = MutableLiveData<RequestState<T>>@MainThreadinline fun <T> StatefulLiveData<T>.observeState(    owner: LifecycleOwner,    init: ResultBuilder<T>.() -> Unit) {    val result = ResultBuilder<T>().apply(init)    observe(owner) { state ->        when (state) {            is RequestState.Loading -> result.onLading.invoke()            is RequestState.Success -> result.onSuccess(state.data)            is RequestState.Error -> result.onError(state.error)        }    }}

应用如下

val data = StatefulMutableLiveData<String>()viewModel.data.observeState(viewLifecycleOwner) {            onLading = {                //loading            }            onSuccess = { data ->                //success            }            onError = { exception ->                //error            }        }

通过以上封装,能够比拟优雅简洁的封装网络申请的loading,success,error状态,精简了代码,构造也比拟清晰

DSL

DSL(domain specific language),即畛域专用语言:专门解决某一特定问题的计算机语言,比方大家耳熟能详的 SQL 和正则表达式。
然而,如果为解决某一特定畛域问题就创立一套独立的语言,开发成本和学习老本都很高,因而便有了外部 DSL 的概念。所谓外部 DSL,便是应用通用编程语言来构建 DSL。比方,本文提到的 Kotlin DSL,咱们为 Kotlin DSL 做一个简略的定义:

“应用 Kotlin 语言开发的,解决特定畛域问题,具备独特代码构造的 API 。”

举个例子,咱们应用TabLayout时,如果要为他增加监听,须要实现以下3个办法

override fun onTabReselected(tab: TabLayout.Tab?){}override fun onTabUnselected(tab: TabLayout.Tab?){}    override fun onTabSelected(tab: TabLayout.Tab?){}

其实咱们个别只会用到onTabSelected办法,其余两个个别是空实现
咱们利用DSL对OnTabSelectedListener进行封装,即可防止写不必要的空实现代码

具体实现如下:

private typealias OnTabCallback = (tab: TabLayout.Tab?) -> Unitclass OnTabSelectedListenerBuilder : TabLayout.OnTabSelectedListener {    private var onTabReselectedCallback: OnTabCallback? = null    private var onTabUnselectedCallback: OnTabCallback? = null    private var onTabSelectedCallback: OnTabCallback? = null    override fun onTabReselected(tab: TabLayout.Tab?) =            onTabReselectedCallback?.invoke(tab) ?: Unit    override fun onTabUnselected(tab: TabLayout.Tab?) =            onTabUnselectedCallback?.invoke(tab) ?: Unit    override fun onTabSelected(tab: TabLayout.Tab?) =            onTabSelectedCallback?.invoke(tab) ?: Unit    fun onTabReselected(callback: OnTabCallback) {        onTabReselectedCallback = callback    }    fun onTabUnselected(callback: OnTabCallback) {        onTabUnselectedCallback = callback    }    fun onTabSelected(callback: OnTabCallback) {        onTabSelectedCallback = callback    }}fun registerOnTabSelectedListener(function: OnTabSelectedListenerBuilder.() -> Unit) =        OnTabSelectedListenerBuilder().also(function)

定义DSL的个别步骤:

  • 1.先定义一个类去实现回调接口,并且实现它的回调办法。
  • 2.察看回调办法的参数,提取成一个函数类型(function type),并且依照须要应用类型别名给函数类型起一个别称,并且用公有润饰。
  • 3.在类外面申明一些可空的函数类型的可变(var)公有成员变量,并且在回调函数中拿到对应的变量实现它的invoke函数,传入对应的参数。
  • 4.在类中定义一些跟回调接口一样名字,然而参数是对应的函数类型的函数,并且将函数类型赋值给以后类的对应的成员变量。
  • 5.定义一个成员函数,参数是一个带有咱们定好那个类的接受者对象并且返回Unit的Lambda表达式,在函数里创立相应的对象,并且应用also函数把Lambda表达式传进去。

调用如下:

tabLayout.addOnTabSelectedListener(registerOnTabSelectedListener {    onTabSelected { vpOrder.currentItem = it?.position ?: 0 }})

如上,就能够防止写一些不必要的空实现代码了

# 相干教程
Android根底系列教程:

Android根底课程U-小结_哔哩哔哩_bilibili

Android根底课程UI-布局_哔哩哔哩_bilibili

Android根底课程UI-控件_哔哩哔哩_bilibili

Android根底课程UI-动画_哔哩哔哩_bilibili

Android根底课程-activity的应用_哔哩哔哩_bilibili

Android根底课程-Fragment应用办法_哔哩哔哩_bilibili

Android根底课程-热修复/热更新技术原理_哔哩哔哩_bilibili

本文转自 https://juejin.cn/post/6921337734216810504,如有侵权,请分割删除。