這是一篇輔助文章,為了輔助大家理解後面的文章,先前先簡單的闡述下kotlin的DSL。
DSL是什麼
DSL指的是特定領域語言,利用kotlin的DSL語言,我們可以寫成更加整潔的程式碼,是程式碼的可閱讀行大幅度提升。基本的概念以及好處我就不再重複闡述了,網上已經有很多類似的文章了,我就通過具體的程式碼示例進行展示好了。
kotlin DSL程式碼長什麼樣
EditText
我還是從最常見EditText
的TextWatcher
說起。
在Java
中當我們需要新增文字輸入監聽的的時候,是如下的寫法:
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
複製程式碼
但是往往有時,我們只需要afterTextChanged
監聽,並不需要其他兩個,但由於介面的限制,我們不得不全部寫出將其實現。導致書寫體驗和閱讀體驗下降。
那麼在kotlin
中我們可以騷操作:
editText.onTextChange {
afterTextChanged { s ->
}
}
複製程式碼
肯定有小夥伴,是這個表情:
那麼這是如何實現的呢,首先,自定義個類,繼承TextWatcher
:
class TextWatcherDsl : TextWatcher {
private var afterTextChanged: ((s: Editable?) -> Unit)? = null
private var beforeTextChanged: ((s: CharSequence?, start: Int, count: Int, after: Int) -> Unit)? =
null
private var onTextChanged: ((s: CharSequence?, start: Int, before: Int, count: Int) -> Unit)? =
null
override fun afterTextChanged(s: Editable?) {
afterTextChanged?.invoke(s)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
beforeTextChanged?.invoke(s, start, count, after)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
onTextChanged?.invoke(s, start, before, count)
}
fun afterTextChanged(after: (s: Editable?) -> Unit) {
afterTextChanged = after
}
fun beforeTextChanged(before: (s: CharSequence?, start: Int, count: Int, after: Int) -> Unit) {
beforeTextChanged = before
}
fun onTextChanged(onChanged: (s: CharSequence?, start: Int, before: Int, count: Int) -> Unit) {
onTextChanged = onChanged
}
}
複製程式碼
這裡面涉及到kotlin
的引數方法內容(即:方法可以直接作為引數傳遞進),不懂自行百度,不再講解。
接下來,來實現一個DSL的擴充套件方法:
inline fun EditText.onTextChange(textWatcher: TextWatcherDsl.() -> Unit): TextWatcher {
val watcher = TextWatcherDsl().apply(textWatcher)
addTextChangedListener(watcher)
return watcher
}
複製程式碼
ok,搞定了,所有的EditText
都具有了onTextChange
方法,監聽文字的方法需要什麼寫什麼。
-
1、擴充套件方法不再具體解釋,不是本文重點
-
2、這裡的
inline
是指內聯方法,即:在編譯時期,程式碼會直接插入掉用處,實際上不是呼叫外部方法。也就不會造成呼叫外部方法而導致的效能開銷,有興趣的小夥伴可以百度了學習。
這裡需要注意理解下引數TextWatcherDsl.() -> Unit
,TextWatcherDsl
是我們剛剛自定義的類,然後後面跟了一個點
,就是說,這個引數可以接收TextWatcherDsl
類裡的所有方法,只要是它裡面的方法都可以傳遞進來。
SharedPreferences
我們再來看下Google官方對於SharedPreferences
的擴充套件。慣例,我們Java
中使用SP寫入資料是這樣的:
SharedPreferences sp = getSharedPreferences("demo", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("key1", "value1");
editor.putString("key2", "value2");
editor.apply();
複製程式碼
在kotlin
中是這樣的:
val sp = getSharedPreferences("demo", Context.MODE_PRIVATE)
sp.edit {
putString("key1", "value1")
putString("key2", "value2")
}
複製程式碼
是不是簡單易讀。
我們來看下官方Google是如何實現的這個方法:
inline fun SharedPreferences.edit(
commit: Boolean = false,
action: SharedPreferences.Editor.() -> Unit
) {
val editor = edit()
action(editor)
if (commit) {
editor.commit()
} else {
editor.apply()
}
}
複製程式碼
是的,沒錯就這麼點。
引數SharedPreferences.Editor
後面跟了一個點
,可以接受SharedPreferences.Editor
中的方法,例如putString
File
上面的寫法,會kotlin
的小夥伴應該都見過了。接下來,我來展示下另一種DSL的寫法,可能你們會很少用。
這裡我操作File檔案的方法來展示。這裡我不再寫java程式碼。
常規寫法
首先我們看一下常規拷貝檔案的寫法:
fun copyTo(form:File, to: File) {
// 具體操作檔案拷貝的程式碼
}
複製程式碼
使用:
copyTo(file1, file2)
複製程式碼
這種太常見了,Java裡也是如此操作的,不細說了。
kotlin擴充套件寫法
接下來改造下,寫成擴充套件方法:
fun File.copyTo(to: File) {
// 具體操作檔案拷貝的程式碼
}
複製程式碼
使用:
file1.copyTo(file2)
複製程式碼
看起來好像舒服了那麼一點
終極寫法
infix fun File.copyTo(to: File) {
// 具體操作檔案拷貝的程式碼
}
複製程式碼
使用:
file1 copyTo file2
複製程式碼
注意看擴充套件方法前面多了一個infix
來修飾方法,是不是非常接近了我們人類說話時的語言方式。
你們的表情是這樣的:
類似的方法,在anko
庫中中也有很多地方使用,多去閱讀原始碼就會發現。
本次的簡介就到此為此,希望通過直接的程式碼展示,能讓各位找到些感覺