前言
家喻户晓,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,如有侵权,请分割删除。