AndroidUtilCodeKTX (如下簡稱 Ktx) 正式開源已經有一個月了。到目前爲止,在 Github 上收穫了 98
個 star 和 11
次 fork。期間上了一次 Github Trending Kotlin 分類的榜單,也收到了一些開發者的好評以及建議。通過這一個月的龜速更新,作了一些我想添加的功能,修復了一些開發者反饋的問題。java
當前最新的版本是 0.0.6
:linux
implementation 'luyao.util.ktx:AndroidUtilKTX:0.0.6'
複製代碼
該版本的 Change log :android
* 增長 log 開關
* 增長 TextView.notEmpty() 擴展方法
* 增長 ShellExt , 提供執行命令行相關函數
* BaseVMFragment、BaseVMActivity 中添加默認異常處理回調
* android 版本判斷
* 判斷無障礙服務是否開啓
* RecyclerView.itemPadding
* 發送郵件
* 文件相關
* BaseActivity/BaseFragment 添加 CoroutineScope 實現
* 自動感知生命週期的 KtxHandler
* Activity 統一管理
* 應用先後臺監聽
* KtxSpan, 封裝常見 Span 的使用
複製代碼
下面簡單介紹一下 0.0.6 版本
的主要修改內容。git
得益於高階函數和 lambda 表達式的使用,咱們能夠充分發揮本身的想象力來封裝一些通用邏輯。例如,對於一個可空對象,非空時執行指定操做,等於空時執行另外一操做,咱們的代碼中可能充斥着大量這種結構的代碼:github
if (obj == null) {
...
} else {
...
}
複製代碼
看看 Ktx 中是如何書寫這種邏輯的:shell
obj.notNull({
...
}, {
...
})
複製代碼
額,好像比原來優雅了那麼一點(允許我欺騙一下本身)。實現也很簡單,以下所示:bash
fun <T> Any?.notNull(f: () -> T, t: () -> T): T {
return if (this != null) f() else t()
}
複製代碼
支持返回值。忽略上面那個不是那麼明顯的優化,順着這個思路,咱們能夠在稍微複雜一點的情景下進行相似的優化。好比,TextView
中的文字是否爲空,能夠定義以下擴展函數 :微信
fun TextView.notEmpty(f: TextView.() -> Unit, t: TextView.() -> Unit) {
if (text.toString().isNotEmpty()) f() else t()
}
複製代碼
若是你想到了更多的使用場景,歡迎來砸 issue。app
這個沒啥特殊的地方,來自個人 Box 項目中讀取 linux 內核版本的需求。雖然說最後也沒讀取成功,但也加了這麼一個頂層函數:mvvm
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"
}
複製代碼
這個來自個人 wanandroid 項目的 issue 7,主要是針對 Kotlin Coroutine 的異常處理。原來也有異常處理,只是 BaseViewModel
中用來接收異常的 mException
是私有的,沒法直接獲取。這個版本作了通用的異常處理,能夠在 BaseVMActivity
和 BaseVMFragment
中直接經過 onError()
回調獲得異常。具體能夠參考 MvvmActivity 中的異常處理演示。
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
複製代碼
工做中遇到的一個小需求,根據無障礙服務名稱判斷服務是否開啓。
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 })
}
複製代碼
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
,內部處理了 dp2px
的邏輯。
IntentExt
中添加了發送郵件的函數,支持最基本的 subject
和 text
。
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)
}
}
複製代碼
文件相關的部分,原本是準備花大把功夫來整合的,但是 Kotlin 標準庫關於文件操做的支持實在是太完善了。我有對照 AndroidUtilCode 中的文件工具類,標準庫基本就能夠知足其中的大部分功能。爲了能更全面的封裝文件管理相關的工具類,我在 Box 項目中添加了簡單的文件管理功能,具體可見最新 commit。
經過這個簡單的文件管理器模塊,在標準庫的基礎上,主要添加了如下擴展屬性和函數。
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 的內容 * [charset] 字符編碼 * / fun File.writeText(append: Boolean = false, text: String, charset: Charset = Charsets.UTF_8) fun File.writeBytes(append: Boolean = false, bytes: ByteArray) /** * [destFile] 目標文件/文件夾 * [overwrite] 是否覆蓋 * [reserve] 是否保留源文件 */
fun File.moveTo(destFile: File, overwrite: Boolean = true, reserve: Boolean = true)
/** * [destFolder] 目標文件/文件夾 * [overwrite] 是否覆蓋 * [func] 單個文件的進度回調 (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)
複製代碼
除此以外的一些常見操做大多已經包含在 Kotlin Stdlib 中,讀者能夠閱讀一下 /kotlin/io/Utils.kt
文件。
在 BaseActivity/BaseFragment
中添加了 CoroutineScope
實現,其子類中能夠直接經過 launch {}
使用主線程的協程做用域,並在 onDestroy()
中會自動取消在該做用域中啓動的協程。有過協程使用經驗的同窗應該不難理解,實現也很簡單。
abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
...
...
override fun onDestroy() {
super.onDestroy()
cancel()
}
}
複製代碼
沒使用過協程的也沒有關係,後續我會寫一篇文章簡單介紹如何正確的在 Android 上使用協程。
這是一個自動感知組件生命週期的 Handler,給它註冊一個 LifecycleOwner
,它就能在組件 onDestroy()
時自動清理資源。這個想法來自我在網上看到的代碼,恕我實在想不起來出處了,下次會提早記錄出處並標註出來。
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 。
這個來源於 issue 區的需求。開發中的需求無非兩個,關閉指定的 Activity,退出應用時關閉全部 Activity ,這兩個函數定義在了單例類 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 中有這兩個函數的使用例子。
這塊的使用仍是比較巧妙的。經過 ActivityLifecycleCallbacks
來監聽 Activity 的生命週期,而 ActivityLifecycleCallbacks 的通常使用方式就是在 Application
中進行註冊。可是在 Ktx 中,並無 Application 類,也無需開發者在集成時書寫任何代碼。它自動完成了生命週期的註冊監聽。可能有的同窗在其餘地方也見識過這個小技巧,若是不知道的話,閱讀一下 Ktx.kt,相信你就明白了。
順帶也經過 ProcessLifecycleOwner
監聽了應用進入前臺和後臺,相信你也看到了對應的 Toast 提示。
最後是一個 Span 工具類,先看看效果。
對這個圖很熟悉的話,說明你是 Blankji 的 AndroidUtilCode 的忠實用戶。這裏我沒有再從新造輪子了,固然也不是徹底照抄 AndroidUtilCode 。我一直是 material-dialogs 的使用者,很是喜歡它的 API 形式,因此仿照它的 API 結構重構了 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(
"測試段落縮進,首行縮進兩字,其餘行不縮進,其餘行不縮進",
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 只有三個方法,但已經足以覆蓋大部分使用場景。
第一個方法 text()
,能夠表示文字的大部分效果。得益於 Kotlin 的方法參數支持默認值的特性,消滅了大部分方法重載的狀況。text() 方法的參數也至關之多,共有 31
個參數,除了 text
爲必須的以外,其他都可以根據需求賦值。默認每一個 text()
方法都是新的一行,你也能夠傳入 isNewLine: Boolean = false
來使得下一行再也不換行。
第二個方法是 image()
,能夠表示圖片顯示,支持屬性有對齊方式和左右間距。
第三個方法是 blockLine()
,表示段落間距。有兩個參數,一個是段落間距的值,一個是是否爲之後的段落都添加間距。
關於 KtxSpan 的具體使用,可見項目中的示例代碼 : KtxSpanActivity 。
以上就是此次更新的所有內容了,AndroidUtilCodeKTX 仍是個孩子,歡迎來撩 !
文章首發微信公衆號:
秉心說
, 專一 Java 、 Android 原創知識分享,LeetCode 題解。AndroidUtilCodeKTX 最新更新信息,掃碼關注我吧!