KTX - 更简洁的开发Android

1,319 阅读4分钟
原文链接: www.jianshu.com

介绍

如果你已经在使用 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 包含了 contentvaluescontext 的扩展函数和 sp 的简化
database 包含了数据库游标的扩展函数和事务处理的简化
graphics 这里面包含的内容很多,主要用于自定义 View,如:PathPointCanvas等等
location Location 对象的解构,可写成 ( lat , lon ) = Location()
net 包含了 urifileString 相互转换
os bundleHandler 的简化写法
text 包含了 StringSpannableString 等等
transition 简化了对 transition 的监听操作
util 这个里面包含了一系列工具类,如:LruCachePairSizeRange 等等
view 包含了三块:ViewViewGroupMenu 三个类的多个方法
widget 这个组件中貌似专门为 EditTextaddTextChangedListener 准备的

实例

我们看看几个常见的用法,对比下在没有使用 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

这部分和动画类似,专门处理 EditTextTextWatcher

// 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 的语法糖,写出来的代码如牛奶般丝滑...

KTX 全部源码

如果本文章你发现有不正确或者不足之处,欢迎你在下方留言或者扫描下方的二维码留言也可!