AndroidUtilCodeKTX !是時候提升你的開發(fā)效率了 P谘骸(更新啦 ?篌荨)

AndroidUtilCodeKTX (以下簡稱 Ktx) 正式開源已經(jīng)有一個月了。到目前為止棚贾,在 Github 上收獲了 98 個 star 和 11 次 fork窖维。期間上了一次 Github Trending Kotlin 分類的榜單,也收到了一些開發(fā)者的好評以及建議妙痹。經(jīng)過這一個月的龜速更新铸史,做了一些我想添加的功能,修復(fù)了一些開發(fā)者反饋的問題怯伊。

當(dāng)前最新的版本是 0.0.6

implementation 'luyao.util.ktx:AndroidUtilKTX:0.0.6'

該版本的 Change log :

* 增加 log 開關(guān)
* 增加 TextView.notEmpty() 擴(kuò)展方法
* 增加 ShellExt , 提供執(zhí)行命令行相關(guān)函數(shù)
* BaseVMFragment琳轿、BaseVMActivity 中添加默認(rèn)異常處理回調(diào)
* android 版本判斷
* 判斷無障礙服務(wù)是否開啟
* RecyclerView.itemPadding
* 發(fā)送郵件
* 文件相關(guān)
* BaseActivity/BaseFragment 添加 CoroutineScope 實現(xiàn)
* 自動感知生命周期的 KtxHandler
* Activity 統(tǒng)一管理
* 應(yīng)用前后臺監(jiān)聽
* KtxSpan, 封裝常見 Span 的使用

下面簡單介紹一下 0.0.6 版本的主要修改內(nèi)容。

一系列空判斷

得益于高階函數(shù)和 lambda 表達(dá)式的使用,我們可以充分發(fā)揮自己的想象力來封裝一些通用邏輯利赋。例如水评,對于一個可空對象,非空時執(zhí)行指定操作媚送,等于空時執(zhí)行另一操作中燥,我們的代碼中可能充斥著大量這種結(jié)構(gòu)的代碼:

if (obj == null) {
    ...
} else {
    ...
}

看看 Ktx 中是如何書寫這種邏輯的:

obj.notNull({
    ...    
}, {
    ...    
})

額,好像比原來優(yōu)雅了那么一點(diǎn)(容許我欺騙一下自己)塘偎。實現(xiàn)也很簡單疗涉,如下所示:

fun <T> Any?.notNull(f: () -> T, t: () -> T): T {
    return if (this != null) f() else t()
}

支持返回值。忽略上面那個不是那么明顯的優(yōu)化吟秩,順著這個思路咱扣,我們可以在稍微復(fù)雜一點(diǎn)的情景下進(jìn)行類似的優(yōu)化。比如涵防,TextView 中的文字是否為空闹伪,可以定義如下擴(kuò)展函數(shù) :

fun TextView.notEmpty(f: TextView.() -> Unit, t: TextView.() -> Unit) {
    if (text.toString().isNotEmpty()) f() else t()
}

如果你想到了更多的使用場景,歡迎來砸 issue壮池。

執(zhí)行 shell 命令

這個沒啥特殊的地方偏瓤,來自我的 Box 項目中讀取 linux 內(nèi)核版本的需求。雖說最后也沒讀取成功椰憋,但也加了這么一個頂層函數(shù):

fun executeCmd(command: String): String {
    val process = Runtime.getRuntime().exec(command)

    val resultReader = BufferedReader(InputStreamReader(process.inputStream))
    val errorReader = BufferedReader(InputStreamReader(process.errorStream))

    val resultBuilder = StringBuilder()
    var resultLine: String? = resultReader.readLine()

    val errorBuilder = StringBuilder()
    var errorLine = errorReader.readLine()

    while (resultLine != null) {
        resultBuilder.append(resultLine)
        resultLine = resultReader.readLine()
    }

    while (errorLine != null) {
        errorBuilder.append(errorLine)
        errorLine = errorReader.readLine()
    }

    return "$resultBuilder\n$errorBuilder"
}

BaseActivity 添加異常處理

這個來自我的 wanandroid 項目的 issue 7厅克,主要是針對 Kotlin Coroutine 的異常處理。原來也有異常處理橙依,只是 BaseViewModel 中用來接收異常的 mException 是私有的证舟,無法直接獲取。這個版本做了通用的異常處理窗骑,可以在 BaseVMActivityBaseVMFragment 中直接通過 onError() 回調(diào)得到異常女责。具體可以參考 MvvmActivity 中的異常處理演示。

Android 版本判斷

fun fromM() = fromSpecificVersion(Build.VERSION_CODES.M)
fun beforeM() = beforeSpecificVersion(Build.VERSION_CODES.M)
fun fromN() = fromSpecificVersion(Build.VERSION_CODES.N)
fun beforeN() = beforeSpecificVersion(Build.VERSION_CODES.N)
fun fromO() = fromSpecificVersion(Build.VERSION_CODES.O)
fun beforeO() = beforeSpecificVersion(Build.VERSION_CODES.O)
fun fromP() = fromSpecificVersion(Build.VERSION_CODES.P)
fun beforeP() = beforeSpecificVersion(Build.VERSION_CODES.P)
fun fromSpecificVersion(version: Int): Boolean = Build.VERSION.SDK_INT >= version
fun beforeSpecificVersion(version: Int): Boolean = Build.VERSION.SDK_INT < version

判斷無障礙服務(wù)是否開啟

工作中遇到的一個小需求慧域,根據(jù)無障礙服務(wù)名稱判斷服務(wù)是否開啟鲤竹。

fun Context.checkAccessbilityServiceEnabled(serviceName: String): Boolean {
    val settingValue =
        Settings.Secure.getString(applicationContext.contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
    return settingValue.notNull({
        var result = false
        val splitter = TextUtils.SimpleStringSplitter(':')
        while (splitter.hasNext()) {
            if (splitter.next().equals(serviceName, true)) {
                result = true
                break
            }
        }
        result
    }, { false })
}

RecyclerView.itemPadding

fun RecyclerView.itemPadding(top: Int, bottom: Int, left: Int = 0, right: Int = 0) {
    addItemDecoration(PaddingItemDecoration(top, bottom, left, right))
}

方便快捷添加 itemPadding 浪读,如下所示:

commonRecycleView.run {
    itemPadding(5, 5, 10, 10)
    layoutManager = LinearLayoutManager(this@CommonListActivity)
    adapter = commonAdapter
}

單位是 dp昔榴,內(nèi)部處理了 dp2px 的邏輯。

發(fā)送郵件

IntentExt 中添加了發(fā)送郵件的函數(shù)碘橘,支持最基本的 subjecttext互订。

fun Context.sendEmail(email: String, subject: String?, text: String?) {
    Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:$email")).run {
        subject?.let { putExtra(Intent.EXTRA_SUBJECT, subject) }
        text?.let { putExtra(Intent.EXTRA_TEXT, text) }
        startActivity(this)
    }
}

文件相關(guān)

文件相關(guān)的部分,本來是準(zhǔn)備花大把功夫來整合的痘拆,可是 Kotlin 標(biāo)準(zhǔn)庫關(guān)于文件操作的支持實在是太完善了仰禽。我有對照 AndroidUtilCode 中的文件工具類,標(biāo)準(zhǔn)庫基本就可以滿足其中的大部分功能。為了能更全面的封裝文件管理相關(guān)的工具類吐葵,我在 Box 項目中添加了簡單的文件管理功能规揪,具體可見最新 commit。

通過這個簡單的文件管理器模塊温峭,在標(biāo)準(zhǔn)庫的基礎(chǔ)上猛铅,主要添加了以下擴(kuò)展屬性和函數(shù)。

val File.canListFiles: Boolean :是否可以獲取子文件夾
val File.totalSize: Long       :總大小凤藏,包括所有子文件夾
val File.formatSize: String    :格式化文件總大小奸忽,包括所有子文件夾
val File.mimeType: String      :獲取文件 mimeType

/**
 * [isRecursive] 是否獲取所有子文件夾
 * [filter] 文件過濾器
 * /
fun File.listFiles(isRecursive: Boolean = false, filter: ((file: File) -> Boolean)? = null): Array<out File> {}

/**
 * [append] 是否追加
 * [text] 要 write 的內(nèi)容
 * [charset] 字符編碼
 * /
fun File.writeText(append: Boolean = false, text: String, charset: Charset = Charsets.UTF_8)
fun File.writeBytes(append: Boolean = false, bytes: ByteArray)

/**
 *   [destFile]  目標(biāo)文件/文件夾
 *   [overwrite] 是否覆蓋
 *   [reserve] 是否保留源文件
 */
fun File.moveTo(destFile: File, overwrite: Boolean = true, reserve: Boolean = true)

/**
 *   [destFolder] 目標(biāo)文件/文件夾
 *   [overwrite] 是否覆蓋
 *   [func] 單個文件的進(jìn)度回調(diào) (from 0 to 100)
 */
fun File.moveToWithProgress(
    destFolder: File,
    overwrite: Boolean = true,
    reserve: Boolean = true,
    func: ((file: File, i: Int) -> Unit)? = null
)

fun File.rename(newName: String)
fun File.rename(newFile: File)

除此之外的一些常見操作大多已經(jīng)包含在 Kotlin Stdlib 中,讀者可以閱讀一下 /kotlin/io/Utils.kt 文件揖庄。

CoroutineScope

BaseActivity/BaseFragment 中添加了 CoroutineScope 實現(xiàn)栗菜,其子類中可以直接通過 launch {} 使用主線程的協(xié)程作用域,并在 onDestroy() 中會自動取消在該作用域中啟動的協(xié)程蹄梢。有過協(xié)程使用經(jīng)驗的同學(xué)應(yīng)該不難理解疙筹,實現(xiàn)也很簡單。

abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {

    ...
    ...
    
    override fun onDestroy() {
        super.onDestroy()
        cancel()
    }
}

沒使用過協(xié)程的也沒有關(guān)系禁炒,后續(xù)我會寫一篇文章簡單介紹如何正確的在 Android 上使用協(xié)程腌歉。

自動感知生命周期的 KtxHandler

這是一個自動感知組件生命周期的 Handler,給它注冊一個 LifecycleOwner齐苛,它就能在組件 onDestroy() 時自動清理資源翘盖。這個想法來自我在網(wǎng)上看到的代碼,恕我實在想不起來出處了凹蜂,下次會提前記錄出處并標(biāo)注出來馍驯。

class KtxHandler(lifecycleOwner: LifecycleOwner, callback: Callback) : Handler(callback), LifecycleObserver {

    private val mLifecycleOwner: LifecycleOwner = lifecycleOwner

    init {
        lifecycleOwner.lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    private fun onDestroy() {
        removeCallbacksAndMessages(null)
        mLifecycleOwner.lifecycle.removeObserver(this)
    }
}

其簡單使用可以參見 LifeCycleActivity.kt

Activity 統(tǒng)一管理/應(yīng)用前后臺監(jiān)聽

這個來源于 issue 區(qū)的需求玛痊。開發(fā)中的需求無非兩個汰瘫,關(guān)閉指定的 Activity,退出應(yīng)用時關(guān)閉所有 Activity 擂煞,這兩個函數(shù)定義在了單例類 KtxManager 中混弥。

fun  finishActivity(clazz: Class<*>){
    for (activity in mActivityList)
        if (activity.javaClass == clazz)
            activity.finish()
}

fun finishAllActivity() {
    for (activity in mActivityList)
        activity.finish()
}

同樣在 LifeCycleActivity.kt 中有這兩個函數(shù)的使用例子。

這塊的使用還是比較巧妙的对省。通過 ActivityLifecycleCallbacks 來監(jiān)聽 Activity 的生命周期蝗拿,而 ActivityLifecycleCallbacks 的一般使用方式就是在 Application 中進(jìn)行注冊。但是在 Ktx 中蒿涎,并沒有 Application 類哀托,也無需開發(fā)者在集成時書寫任何代碼。它自動完成了生命周期的注冊監(jiān)聽劳秋〔质郑可能有的同學(xué)在其他地方也見識過這個小技巧胖齐,如果不知道的話,閱讀一下 Ktx.kt嗽冒,相信你就明白了呀伙。

順帶也通過 ProcessLifecycleOwner 監(jiān)聽了應(yīng)用進(jìn)入前臺和后臺,相信你也看到了對應(yīng)的 Toast 提示添坊。

KtxSpan

最后是一個 Span 工具類区匠,先看看效果。

對這個圖很熟悉的話帅腌,說明你是 Blankji 的 AndroidUtilCode 的忠實用戶驰弄。這里我沒有再重新造輪子了,當(dāng)然也不是完全照抄 AndroidUtilCode 速客。我一直是 material-dialogs 的使用者戚篙,非常喜歡它的 API 形式,所以仿照它的 API 結(jié)構(gòu)重構(gòu)了 KtxSpan溺职。

KtxSpan().with(spanTv).show {
            text(
                "SpanUtils",
                foregroundColor = Color.YELLOW,
                backgroundColor = Color.LTGRAY,
                alignment = Layout.Alignment.ALIGN_CENTER
            )
            text("前景色", foregroundColor = Color.GREEN)
            text("背景色", backgroundColor = Color.LTGRAY)
            blockLine(spanTv.dp2px(6), true)
            text("行高", lineHeight = 2 * spanTv.lineHeight, backgroundColor = Color.LTGRAY)
            text(
                "測試段落縮進(jìn)岔擂,首行縮進(jìn)兩字,其他行不縮進(jìn)浪耘,其他行不縮進(jìn)",
                first = (spanTv.textSize * 2).toInt(),
                rest = 0,
                backgroundColor = Color.GREEN
            )
            text(
                "測試引用乱灵,后面的字是為了湊到兩行的效果",
                quoteColor = Color.GREEN,
                quoteStripeWidth = 10,
                quoteGapWidth = 10,
                backgroundColor = Color.LTGRAY
            )
            image(
                R.drawable.small,
                verticalAlignment = ImageSpan.ALIGN_BOTTOM,
                marginLeft = dp2px(30),
                marginRight = dp2px(40)
            )
}

KtxSpan 只有三個方法,但已經(jīng)足以覆蓋大部分使用場景七冲。

第一個方法 text() 痛倚,可以表示文字的大部分效果。得益于 Kotlin 的方法參數(shù)支持默認(rèn)值的特性澜躺,消滅了大部分方法重載的情況蝉稳。text() 方法的參數(shù)也相當(dāng)之多,共有 31 個參數(shù)掘鄙,除了 text 為必須的之外耘戚,其余均可以根據(jù)需求賦值。默認(rèn)每個 text() 方法都是新的一行操漠,你也可以傳入 isNewLine: Boolean = false 來使得下一行不再換行收津。

第二個方法是 image() ,可以表示圖片顯示浊伙,支持屬性有對齊方式和左右間距撞秋。

第三個方法是 blockLine(),表示段落間距吧黄。有兩個參數(shù)部服,一個是段落間距的值唆姐,一個是是否為以后的段落都添加間距拗慨。

關(guān)于 KtxSpan 的具體使用,可見項目中的示例代碼 : KtxSpanActivity

Last

以上就是這次更新的全部內(nèi)容了赵抢,AndroidUtilCodeKTX 還是個孩子剧蹂,歡迎來撩 !

文章首發(fā)微信公眾號: 秉心說 烦却, 專注 Java 宠叼、 Android 原創(chuàng)知識分享,LeetCode 題解其爵。

AndroidUtilCodeKTX 最新更新信息冒冬,掃碼關(guān)注我吧!

green.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末摩渺,一起剝皮案震驚了整個濱河市简烤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌摇幻,老刑警劉巖横侦,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绰姻,居然都是意外死亡枉侧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門狂芋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來榨馁,“玉大人,你說我怎么就攤上這事帜矾×居埃” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵黍特,是天一觀的道長蛙讥。 經(jīng)常有香客問我,道長灭衷,這世上最難降的妖魔是什么次慢? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮翔曲,結(jié)果婚禮上迫像,老公的妹妹穿的比我還像新娘。我一直安慰自己瞳遍,他們只是感情好闻妓,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掠械,像睡著了一般由缆。 火紅的嫁衣襯著肌膚如雪注祖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天均唉,我揣著相機(jī)與錄音是晨,去河邊找鬼。 笑死舔箭,一個胖子當(dāng)著我的面吹牛罩缴,可吹牛的內(nèi)容都是我干的恬吕。 我是一名探鬼主播褂微,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嫡锌!你這毒婦竟也來了镜会?” 一聲冷哼從身側(cè)響起炉抒,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稚叹,沒想到半個月后焰薄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扒袖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年塞茅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片季率。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡野瘦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出飒泻,到底是詐尸還是另有隱情鞭光,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布泞遗,位于F島的核電站惰许,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏史辙。R本人自食惡果不足惜汹买,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望聊倔。 院中可真熱鬧晦毙,春花似錦、人聲如沸耙蔑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽甸陌。三九已至须揣,卻和暖如春盐股,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背返敬。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工遂庄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寥院,地道東北人劲赠。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像秸谢,于是被迫代替她去往敵國和親凛澎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容