用Kotlin的Anko庫優雅開發Android應用--Anko庫詳細教程

前言

最近Kotlin的呼聲又是日益高漲,前幾天9012年Google IO正式將Kotlin從first _class提升爲kotlin_first。我也是接觸了一段時間的Kotlin,給我的感覺就是簡約,快速。無需繁瑣的findId,高階函數的應用,再加上Kotlin的null 安全,更是將代碼的崩潰率降到更低。

今天我們就來介紹一下今天的主角—Anko

1.Anko

Anko是JetBrains開發的一個強大的庫,說起JetBrains ,那就牛逼了,Kotlin語言是他們開發的,最流行的的開發工具intellij idea都是他們開發的,AS也是基於IDEA的。好了,言歸正傳,Anko是Kotlin官方開發的一個讓開發Android應用更快速更簡單的Kotlin庫,並且能讓我們書寫的代碼更簡單清楚更容易閱讀。它包括多個部分

  1. Anko Commons: a lightweight library full of helpers for intents, dialogs, logging and so on;
  2. Anko Layouts: a fast and type-safe way to write dynamic Android layouts;
  3. Anko SQLite: a query DSL and parser collection for Android SQLite;
  4. Anko Coroutines: utilities based on the kotlinx.coroutines library.

1.1 如何使用

添加依賴

dependencies {
    implementation "org.jetbrains.anko:anko:$anko_version"
}

這裏麪包括上面四個部分,當然你也可以只依賴一個部分,如下:

dependencies {
    // Anko Commons
    implementation "org.jetbrains.anko:anko-commons:$anko_version"

    // Anko Layouts
    implementation "org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21, sdk23 are also available
    implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"

    // Coroutine listeners for Anko Layouts
    implementation "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
    implementation "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"

    // Anko SQLite
    implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
}

下面我們分別介紹這幾個功能。

2 AnkoCommons

AnkoCommons對Android開發者來說是一個工具集,包括但不限於下面這幾個

  1. Intents
  2. Dialogs and toasts
  3. Logging
  4. Resources and dimensions

2.1 Intents

前面已經提到,Commons 庫是一個工具集,那Intents主要是幫助簡化Activity之間的跳轉。

傳統的 Kotlin 啓動新的 Activity 的方式是創建一個 Intent,同時可能傳遞一些參數,最後將創建的 Intent 通過 Context 的 startActivity() 方法傳遞,就像這樣:

val intent = Intent(this, SomeOtherActivity::class.java)
intent.putExtra("id", 5)
intent.setFlag(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(intent)

然鵝你用Anko只需要這樣:

startActivity(intentFor(“id” to 5).singleTop())

如果想要傳遞多個參數,你也可以這樣

startActivity<SomeOtherActivity>(
    "id" to 5,
    "city" to "Denpasar"
)

當然還有一些關於Intent的其它操作,如:撥打電話等:如下

在這裏插入圖片描述

2.2 Dialogs and toasts

這個庫主要是用來快速搭建Dialog和toast,具體包含以下幾個

  • Toast
  • SnackBar
  • Alert (Dialog)
  • Selectors
  • Progress dialogs

2.2.1 Toast

Anko爲我們提供了更加簡單的Toast使用,只需要一行代碼即可實現

toast("Hi there!")
toast(R.string.message)
longToast("Wow, such duration")

2.2.2 SnackBars

SnackBar是 Android Support Library 22.2.0 裏面新增提供的一個控件,我們可以簡單的把它理解成一個加強版的Toast,或者是一個輕量級的Dialog。
我們可以用下面代碼快速創建snackbar。

view.snackbar("Hi there!")
view.snackbar(R.string.message)
view.longSnackbar("Wow, such duration")
view.snackbar("Action, reaction", "Click me!") { doStuff() }

這裏需要傳入view對象,這個可以是佈局中的任意一個view對象。

2.2.3 Alerts

Anko Alerts主要包括以下幾個功能:

  1. Android 默認dialog
  2. Android Appcompat 中AlertDialog
  3. 自定義Dialog

1.Android 默認dialog
通過以下代碼就可以構建一個可以交互的Android 默認dialog。

alert("Hi, I'm Roy", "Have you tried turning it off and on again?") {
    yesButton { toast("Oh…") }
    noButton {}
}.show()

代碼比較簡單,就不做解釋。

2.Android Appcompat 中AlertDialog
另外Anko還提供了Appcompat的AlertDialog實現方式,如下:

alert(Appcompat, "Some text message").show()

3.自定義Dialog
什麼,不能自定義dialog嗎?怎麼會,自定義dialog也是非常的簡單

alert {
    customView {
        editText()
    }
}.show()

2.2.4 Selectors (包含列表的Dialog)

我們平時創建列表Dialog是這樣的:

  val listItems = arrayOf("Russia", "USA", "Japan", "Australia")  //傳數組
        val listDialog: AlertDialog.Builder = AlertDialog.Builder(this)
        listDialog.setItems(listItems) { p0, p1 ->
        	toast(p1)
        }
        val dialog: AlertDialog = listDialog.create()
        dialog.show()
        val window: Window = dialog.window
        val params: WindowManager.LayoutParams = window.attributes
        params.y = 45 * ScreenUtils.getScreenDensity().toInt()
        params.gravity = Gravity.TOP or Gravity.RIGHT
        params.width = ScreenUtils.getScreenWidth() / 2
        params.height = ViewGroup.LayoutParams.WRAP_CONTENT
        window.attributes = params

但是我們用Anko是這樣的:

val countries = listOf("Russia", "USA", "Japan", "Australia")  //傳list
selector("Where are you from?", countries, { dialogInterface, i ->
    toast("So you're living in ${countries[i]}, right?")
})

看起來只是簡化了dialog的創建過程。

2.2.5 Progress dialogs

不顯示進度的 Loading Dialg

pressDialog("Please wait a minute.", "Downloading…")
indeterminateProgressDialog("Fetching the data…")

2.3 Logging

打印log輔助工具。

Android SDK 提供 android.util.Log 類來提供一些 logging 方法,,這些方法都很實用,但是我們每次必須傳遞一個 Tag 參數,同時這個 Tag 信息必須是 String 類型的,這就略顯麻煩。不過現在我們可以通過 AnkoLogger 類擺脫這些惱人的問題:

class SomeActivity : Activity(), AnkoLogger {
    fun someMethod() {
        info("Info message")
        debug(42) // .toString() method will be called automatically
    }
}

默認的 Tag 名是當前的類名( 本例中的是SomeActivity),但是通過重寫 AnkoLogger 的 loggerTag 屬性我們是可以來更改的,而且每個方法有兩個版本:plain and lazy (inlined)

1.Lazy:

info("String " + "concatenation")
info { "String " + "concatenation" }

2.plain:

class SomeActivity : Activity() {
    private val log = AnkoLogger(this.javaClass)
    private val logWithASpecificTag = AnkoLogger("my_tag")

    private fun someMethod() {
        log.warning("Big brother is watching you!")
    }
}

上面兩種方法分別是不同Tag的實現方式。

AnkoLogger中loggerTag 屬性具體對照如下:
在這裏插入圖片描述
2.4 Resources and dimensions

你可以在你的項目中使用Anko Resources and dimensions來簡化你的代碼,例如Color、Dimen等,顏色透明度直接色值.opaque就可以,尺寸的話直接使用dip(dipValue)、sp(spValue)就可以。在這裏面還有一個就是applyRecursively()用來控制子View的操作,如:

  verticalLayout {
        textView{
            text = "EditText01"
            backgroundColor = 0xff000.opaque
            textSize = 14f
        }
        textView {
            text = "EditText02"
            backgroundColor = 0x99.gray.opaque
            textSize = 23f
        }
    }.applyRecursively {//如果是ViewGroup的話可以使用applyRecursively來爲每個Child View進行設置
        view -> when(view){
            is TextView -> view.textColor = Color.RED
        }
    }

3.Anko Layouts

通常我們使用xml文件寫我們的佈局,但是他有一些缺點如不是類型安全,不是空安全,解析xml文件消耗更多的CPU和電量等等。而Anko Layout可以使用DSL(Domain Specific Language)動態創建我們的UI,並且它比我們使用Java動態創建佈局方便很多主要是更簡潔,它和擁有xml創建佈局的層級關係,能讓我們更容易閱讀。(官方說的優點)

舉個栗子:

verticalLayout {
    val name = editText()
    button("Say Hello") {
        onClick { toast("Hello, ${name.text}!") }
    }
}

上面的代碼是不是很簡單易懂,當然,默認的控件並不能滿足我們的需求,例如我們會更改字體的顏色及大小,會設置寬度和高度,會設置margin,padding值,那麼該如何實行呢,當然也很簡單,因爲它的邏輯和xml書寫佈局是一個套路。例如以下實現

val textView=textView("我是一個TextView"){
                textSize = sp(17).toFloat()
                textColor=0xff000.opaque
            }.lparams{
                margin=dip(10)
                height= dip(40)
                width= matchParent
            }

配合上前面Common庫是不是很簡單呢?

這裏我們不需要setContentView。直接寫在onCreate方法中就行。

在上面創建UI過程中,我們直接把創建UI的代碼寫在onCreate方法中了,當然,還有一種寫法。我們創建一個內部類實行AnkoComponent接口,並重寫createView方法,該方法返回一個View,也就是我們創建的佈局。修改如下

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)
        MyActivityUI().setContentView(this)
    }
}

class MyActivityUI : AnkoComponent<MyActivity> {
    override fun createView(ui: AnkoContext<MyActivity>) = with(ui) {
        verticalLayout {
            val name = editText()
            button("Say Hello") {
                onClick { ctx.toast("Hello, ${name.text}!") }
            }
        }
    }
}

現在我們編譯運行,發現效果和佈局文件寫的界面是一樣的。但是它的性能是有優勢的,其實吧並沒有發覺性能優勢。不管怎樣,這種DSL確實便於閱讀,也很容易上手,在上面的代碼中,你可能注意到了dip(10),它表示將10dp轉換爲像素的意思,是Anko的擴展函數,說的擴展函數,如果閱讀過Anko的源碼我們發現裏面大量的使用擴展函數,這也是Kotlin語言的優勢之一。

這裏就簡單介紹下Layout的使用和優點。但是我想各位看官在實際開發中也不一定會用,因爲不可視化用起來實在難以接受。不過這種見仁見智吧。

4.Anko SQLite: a query DSL and parser collection for Android SQLite;

Anko SQLite是一個查詢解析SQLite的領域專用語言

SQLite 存在的不足:

  1. 過多需要實現的模板代碼;
  2. 通過字符串實現 SQL 命令,容易出錯,且編譯時無法檢查;
  3. 每次需要手動關閉數據庫;
  4. 線程和同步訪問的問題;

隨着你應用的數據庫越來越複雜,暴露出來的問題也會越多。所以也難怪很多人會選擇 ORM 或者 NoSQL,但這帶來的方便性是以增加應用大小和方法數爲代價的。

如果你打算使用 Kotlin 來開發 Android 應用,那麼現在通過 Anko SQLite 就可以很方便的進行 SQLite 操作了。比如可以告別麻煩的 Cursor 和 ContentValue、內置安全機制,保證數據庫在執行所有代碼後能夠關閉。

通過繼承 ManagedSQLiteOpenHelper 類來實現數據庫幫助類,推薦做法就是按照官方的實現:

class DatabaseHelper(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "LibraryDatabase", null, 1) {
  companion object {
    private var instance: DatabaseHelper? = null

    @Synchronized
    fun Instance(context: Context): DatabaseHelper {
      if (instance == null) {
        instance = DatabaseHelper(context.applicationContext)
      }
      return instance!!
    }
  }

  override fun onCreate(database: SQLiteDatabase) {
    createTable(Book.TABLE_NAME, true, Book.COLUMN_ID to INTEGER + PRIMARY_KEY, Book.COLUMN_TITLE to TEXT, Book.COLUMN_AUTHOR to TEXT)
  }

  override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
    dropTable(Book.TABLE_NAME, true)
  }
}

訪問數據庫的推薦做法是通過依賴注入,或者爲 Context 添加一個 extension:

val Context.database: DatabaseHelper
  get() = DatabaseHelper.Instance(applicationContext)

下面這是一個簡單的 model 類:

data class Book(val id: Int, val title: String, val author: String) {
  companion object {
    val Book.COLUMN_ID = "id"
    val TABLE_NAME = "books"
    val COLUMN_TITLE = "title"
    val COLUMN_AUTHOR = "author"
  }
}

當數據庫準備好後,就可以通過 use 方法來進行操作了。比如:

database.use {
    insert(Book.TABLE_NAME, Book.COLUMN_ID to 1, Book.COLUMN_TITLE to "2666", Book.COLUMN_AUTHOR to "Roberto Bolano")
}

最後,讓我們來比較一下常見庫的大小:

在這裏插入圖片描述

要注意這裏 Anko SQLite 的依賴大小其實是包含了 Kotlin Runtim (method count: 6298, DEX size: 1117 KB) 和 Anko Commons module (method count: 982, DEX size: 174 KB) 的。因此如果你是通過 Kotlin 來開發,Anko SQLite 增加的大小其實並沒有圖表顯示得那麼多。

因此,如果你在使用 Kotlin 做開發並且數據庫的複雜度不高,首推 Anko SQLite。但如果數據庫結構非常複雜,DAO 和 SQL 查詢可能會變得很痛苦,這時候 ORM 和 NoSQL 就是首選方案了。

5.Anko Coroutines: utilities based on the kotlinx.coroutines library.

Kotlin協程
協程本質上是一個輕量級的線程,支持我們用同步寫法寫異步請求,而不用Callback。

doAsync(UI) {
    val data: Deferred<Data> = bg {
		// Runs in background
		getData()
    }

    // This code is executed on the UI thread
    showData(data.await())
}

我們可以用bg()在新的線程去做耗時操作,等返回結果再在UI線程進行操作。

具體協程教程我們可以看一下官方文檔

Kotlin協程

6.總結

Anko是Kotlin官方開發的一個讓開發Android應用更快速更簡單的Kotlin庫,並且能讓我們書寫的代碼更簡單清楚更容易閱讀。
主要包含了以下幾個部分。

  1. Anko Commons: a lightweight library full of helpers for intents, dialogs, logging and so on;
  2. Anko Layouts: a fast and type-safe way to write dynamic Android layouts;
  3. Anko SQLite: a query DSL and parser collection for Android SQLite;
  4. Anko Coroutines: utilities based on the kotlinx.coroutines library.

Anko庫用起來非常的簡單方便,裏面設計到DSL和擴展函數。

關於DSL可以看下這篇文章

Kotlin 之美—DSL篇

關於擴展函數可以看下這篇文章

Kotlin 擴展函數詳解與應用

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