介绍
如果你已经在使用 Kotlin 开发 Android 了,相信你深深的体会到了 Kotlin 的简洁、高效。不止如此,Google 为了为了让开发者更好的利用 Kotlin 语言能力(例如扩展函数/属性、lambda、命名参数和参数默认值),特意推出了 Android KTX,它是一组 Kotlin 扩展程序,可以以更简洁、更愉悦、更惯用的方式使用 Kotlin 进行 Android 开发。
使用
要将 KTX 加入到项目中,需要向 build.gradle 中添加依赖:
dependencies {
// 目前最新版本为1.0.2
implementation 'androidx.core:core-ktx:1.0.2'
}
这里附上官方和 GitHub 地址:
官方教程
GitHub
目录
目前 KTX 已经包含了12个主目录(KTX1.0.2 版本):
| 目录名 | 包含的内容 |
|---|---|
| animation | 简化了动画监听事件的写法 |
| content | 包含了 contentvalues、context 的扩展函数和 sp 的简化 |
| database | 包含了数据库游标的扩展函数和事务处理的简化 |
| graphics | 这里面包含的内容很多,主要用于自定义 View,如:Path、Point、Canvas等等 |
location |
对 Location 对象的解构,可写成 ( lat , lon ) = Location() |
| net | 包含了 uri、file 和 String 相互转换 |
| os | 有 bundle 和 Handler 的简化写法 |
| text | 包含了 String、SpannableString 等等 |
| transition | 简化了对 transition 的监听操作 |
| util | 这个里面包含了一系列工具类,如:LruCache、Pair、Size 和 Range 等等 |
| view | 包含了三块:View、ViewGroup 和 Menu 三个类的多个方法 |
widget |
这个组件中貌似专门为 EditText 的 addTextChangedListener 准备的 |
实例
我们看看几个常见的用法,对比下在没有使用 KTX 和使用 KTX 后的变化
1) SP
val sp: SharedPreferences = getSharedPreferences("sp", Context.MODE_PRIVATE)
// 常规
val editor = sp.edit()
editor.putString("name", "taonce").apply()
editor.putString("name", "tao").apply()
// KTX
sp.edit {
putString("name", "taonce")
putString("name", "tao")
}
这里就拿 SharePreferences.edit() 来看看 KTX 的实现,后面的很多大家请自行查看源码,大多都是采用了扩展函数和lambda 来实现的。
@SuppressLint("ApplySharedPref")
inline fun SharedPreferences.edit(
// 标志是采用 commit 还是 apply
commit: Boolean = false,
// 这是一个Editor的扩展函数,采用lambda闭包
action: SharedPreferences.Editor.() -> Unit
) {
// 获取editor对象
val editor = edit()
// 执行lambda
action(editor)
// 判断
if (commit) {
editor.commit()
} else {
editor.apply()
}
}
2)获取系统服务
KTX 采用泛型来表明获取的服务类型,不需要再强转
// context的扩展函数
// 常规获取系统服务
val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
// KTX
val windowManagerKT = getSystemService<WindowManager>()
// 源码
inline fun <reified T : Any> Context.getSystemService(): T? =
ContextCompat.getSystemService(this, T::class.java)
3)ContentValues
在 ContentValues 的操作上,则是用到了 apply() 操作符
// ContentValues
// 常规
val commonContentValues = ContentValues()
commonContentValues.put("name", "taonce")
commonContentValues.put("age", 20)
// KTX
val contentValues: ContentValues = contentValuesOf(
// 只需要传入键值对
Pair("name", "taonce"),
"age" to 20
)
// 源码
fun contentValuesOf(vararg pairs: Pair<String, Any?>) = ContentValues(pairs.size).apply {
for ((key, value) in pairs) {
when (value) {
null -> putNull(key)
is String -> put(key, value)
is Int -> put(key, value)
is Long -> put(key, value)
is Boolean -> put(key, value)
is Float -> put(key, value)
is Double -> put(key, value)
is ByteArray -> put(key, value)
is Byte -> put(key, value)
is Short -> put(key, value)
else -> {
val valueType = value.javaClass.canonicalName
throw IllegalArgumentException("Illegal value type $valueType for key \"$key\"")
}
}
}
}
4)Animator动画
// Animator
val animator = ObjectAnimator.ofFloat(tv, "alpha", 1.0f, 0.2f)
// 常规
animator.addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationEnd(animation: Animator?) {
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
}
})
// KTX
animator.doOnStart { }
animator.doOnEnd { }
animator.doOnCancel { }
// 源码比较长,大家自行阅读,实现方式为:lambda + 参数的默认值
5)Handler
省去了让我们自己创建 Runnable 对象的操作
// 常规
handler.postDelayed({
// runnable.run()
}, 1000L)
// KTX
handler.postDelayed(1000L) {
// runnable.run()
}
// 源码
inline fun Handler.postDelayed(
delayInMillis: Long,
token: Any? = null,
crossinline action: () -> Unit
): Runnable {
// 内部创建了Runnable,将lambda传入
val runnable = Runnable { action() }
if (token == null) {
postDelayed(runnable, delayInMillis)
} else {
HandlerCompat.postDelayed(this, runnable, token, delayInMillis)
}
return runnable
}
6)LRUCache
KTX 在创建 LRUCache 帮助我们省去了繁琐的 object
// lruCache
// 常规
val commonLruCache = object : LruCache<String, Bitmap>(50) {
override fun sizeOf(key: String, value: Bitmap): Int {
return (value.rowBytes) * (value.height) / 1024
}
}
// KTX
val lruCache: LruCache<String, Bitmap> = lruCache(50, sizeOf = { _, value ->
return@lruCache (value.rowBytes) * (value.height) / 1024
})
// 源码,还是结合lambda + 参数默认值实现
inline fun <K : Any, V : Any> lruCache(
maxSize: Int,
crossinline sizeOf: (key: K, value: V) -> Int = { _, _ -> 1 },
@Suppress("USELESS_CAST") // https://youtrack.jetbrains.com/issue/KT-21946
crossinline create: (key: K) -> V? = { null as V? },
crossinline onEntryRemoved: (evicted: Boolean, key: K, oldValue: V, newValue: V?) -> Unit =
{ _, _, _, _ -> }
): LruCache<K, V> {
return object : LruCache<K, V>(maxSize) {
override fun sizeOf(key: K, value: V) = sizeOf(key, value)
override fun create(key: K) = create(key)
override fun entryRemoved(evicted: Boolean, key: K, oldValue: V, newValue: V?) {
onEntryRemoved(evicted, key, oldValue, newValue)
}
}
}
7)EditText
这部分和动画类似,专门处理 EditText 的 TextWatcher
// EditText
val editText = EditText(this)
// 常规 必须实现TextWatcher接口
editText.addTextChangedListener(object : TextWatcher{
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
// KTX 可以实现某一个监听事件
editText.doOnTextChanged { text, start, count, after ->
print("text is $text, start index is $start, text len is $count, changed index is $after")
}
editText.doAfterTextChanged { }
editText.doBeforeTextChanged { text, start, count, after -> }
// KTX源码自行分析哦,太长了
8)Location
最新说一个 Location,它的实现方式和上面都有所不同,采用了 Kotlin 的解构
// Location
val location = Location("")
// 可将location解构成(lat,lon)
val (lat, lon) = location
// 源码
inline operator fun Location.component1() = this.latitude
inline operator fun Location.component2() = this.longitude
精力有限,举了常用的8个案例来说明 KTX,不过相信大家看完之后就完全明白了,哈哈。使用 KTX 在日常开发中还是蛮舒服的,结合 Kotlin 的语法糖,写出来的代码如牛奶般丝滑...
如果本文章你发现有不正确或者不足之处,欢迎你在下方留言或者扫描下方的二维码留言也可!
