KTX - 更簡潔的開發Android

介紹

如果你已經在使用 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 全部源碼

如果本文章你發現有不正確或者不足之處,歡迎你在下方留言或者掃描下方的二維碼留言也可!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章