乐趣区

关于an-d-ro-id:你应该知道的kotlin实用技巧

前言

家喻户晓,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 定义一个扩大函数

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

// Caller
startActivity<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>>

@MainThread
inline 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?) -> Unit

class 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,如有侵权,请分割删除。

退出移动版