使用Kotlin高效地開發Android App(二)

旋轉的樓梯.jpg

上一篇文章介紹了項目中所使用的Kotlin特性,本文繼續整理當前項目所用到的特性。java

一.apply 函數 和 run 函數

with、apply、run函數都是Kotlin標準庫中的函數。with在第一篇文章中已經介紹過。android

1.1 apply函數

apply函數是指在函數塊內能夠經過 this 指代該對象,返回值爲該對象本身。在鏈式調用中,能夠考慮使用它來不破壞鏈式。git

/** * Calls the specified function [block] with `this` value as its receiver and returns `this` value. */
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
複製代碼

舉個例子:github

/** * Created by tony on 2018/4/26. */
object Test {

    @JvmStatic
    fun main(args: Array<String>) {

        val result ="Hello".apply {


            println(this+" World")

            this+" World"
        }

        println(result)
    }
}
複製代碼

執行結果:閉包

Hello World
Hello
複製代碼

第一個字符串是在閉包中打印的,第二個字符串是result的結果,它仍然是「Hello」。架構

1.2 run函數

run函數相似於apply函數,可是run函數返回的是最後一行的值。app

/** * Calls the specified function [block] with `this` value as its receiver and returns its result. */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
複製代碼

舉個例子:函數

/** * Created by tony on 2018/4/26. */
object Test {

    @JvmStatic
    fun main(args: Array<String>) {

        val result ="Hello".run {


            println(this+" World")

            this + " World"
        }

        println(result)
    }
}
複製代碼

執行結果:工具

Hello World
Hello World
複製代碼

第一個字符串是在閉包中打印的,第二個字符串是result的結果,它返回的是閉包中最後一行的值,因此也打印「Hello World」。佈局

1.3 項目中的使用

在App的反饋頁面中,須要輸入郵箱、主題、內容才能完成反饋按鈕的提交。

最初的寫法是這樣:

if (viewModel.email.value!!.isEmpty()) {
            toast(resources.getString(R.string.you_have_not_completed_the_email_address)).show()
            return@onClickRight
        }
        if (!Util.checkEmail(viewModel.email.value!!)) {
            toast(resources.getString(R.string.the_email_format_you_have_filled_is_incorrect)).show()
            return@onClickRight
        }
        if (viewModel.subject.value!!.isEmpty()) {
            toast(resources.getString(R.string.you_have_not_completed_the_feedback_subject)).show()
            return@onClickRight
        }
        if (viewModel.content.value!!.isEmpty()) {
            toast(resources.getString(R.string.you_have_not_completed_the_details)).show()
            return@onClickRight
        }
複製代碼

修改爲只使用apply函數

viewModel.apply {

            if (email.value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_email_address)).show()
                return@onClickRight
            }
            if (!Util.checkEmail(email.value!!)) {
                toast(resources.getString(R.string.the_email_format_you_have_filled_is_incorrect)).show()
                return@onClickRight
            }
            if (subject.value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_feedback_subject)).show()
                return@onClickRight
            }
            if (content.value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_details)).show()
                return@onClickRight
            }
        }
複製代碼

感受不夠cool,能夠結合run和apply函數一塊兒使用

viewModel.email.run {

            if (value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_email_address)).show()
                return@onClickRight
            }
            if (!Util.checkEmail(value!!)) {
                toast(resources.getString(R.string.the_email_format_you_have_filled_is_incorrect)).show()
                return@onClickRight
            }

            viewModel
        }.subject.run {

            if (value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_feedback_subject)).show()
                return@onClickRight
            }

            viewModel
        }.content.apply {

            if (value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_details)).show()
                return@onClickRight
            }
        }
複製代碼

二.data class

Kotlin的data class有點相似於Scala的case class。

如下的Java Bean代碼

/** * Created by tony on 2018/4/27. */
public class User {

    public String userName;
    public String password;
}
複製代碼

等價於

data class User (var userName: String? = null,var password: String? = null)
複製代碼

能夠看到採用data class可以簡化Java Bean類。咱們的App採用了MVVM的架構,所以對應Model類所有使用data class。

三.無需使用findViewById 或者 butterknife

使用Kotlin Android Extensions插件便可實現該功能,它是Kotlin 插件的組成之一,無需再單獨安裝插件。

咱們在各個modules的build.gradle中添加該插件,便可使用。

apply plugin: 'kotlin-android-extensions'
複製代碼

佈局文件中的id,能夠直接在代碼中使用。 首先,按照import kotlinx.android.synthetic.main.佈局文件名*的方式導入。

例如MainActivity,它的佈局文件是activity_main.xml 則按照以下的方式進行import

import kotlinx.android.synthetic.main.activity_main.*
複製代碼

那麼activity_main.xml中控件的id,能夠直接在MainActivity中使用,無需使用findViewById 或者 butterknife。是否是特別方便?

四.點擊事件的埋點處理

App的埋點,使用本身家的產品--魔窗的sdk來作事件的埋點。

若是使用Java來開發App,可使用AOP來實現埋點。因爲咱們的App採用Kotlin編寫,Kotlin能夠將事件的點擊簡化成以下的形式

view.setOnClickListener {
             ....
        }
複製代碼

這種簡化了的lambda表達式,因此我仍是老老實實的使用傳統方式進行埋點。

使用Kotlin的一般作法:

view.setOnClickListener {

             TrackAgent.currentEvent().customEvent(eventName)
             ....
        }
複製代碼

或者

view.setOnClickListener {

             TrackAgent.currentEvent().customEvent(eventName, trackMap)
             ....
        }
複製代碼

後來,我寫了一個View的擴展函數click,後來通過同事的優化。能夠查看簡書的這篇文章 利用擴展函數優雅的實現「防止重複點擊」

目前,已經將該擴展函數放入個人Kolin的工具類庫 https://github.com/fengzhizi715/SAF-Kotlin-Utils

此時,埋點的代碼變成這樣

view.click {

             TrackAgent.currentEvent().customEvent(eventName)
             ....
        }
複製代碼

或者

view.click {

             TrackAgent.currentEvent().customEvent(eventName, trackMap)
             ....
        }
複製代碼

進一步的優化處理,對於View增長擴展函數clickWithTrack專門用於埋點的點擊事件。

package cn.magicwindow.core.ext

import android.view.View
import cn.magicwindow.TrackAgent
import com.safframework.ext.clickWithTrigger

/** * * @FileName: * cn.magicwindow.core.ext.ViewExt.java * @author: Tony Shen * @date: 2018-04-24 17:17 * @version V1.0 <描述當前版本功能> */

fun <T : View> T.clickWithTrack(eventName: String, time: Long = 600, block: (T) -> Unit) = this.clickWithTrigger(time) {

    TrackAgent.currentEvent().customEvent(eventName)
    block(it as T)
}

fun <T : View> T.clickWithTrack(eventName: String, trackMap: HashMap<String, String>, time: Long = 600, block: (T) -> Unit) = this.clickWithTrigger(time) {

    TrackAgent.currentEvent().customEvent(eventName, trackMap)
    block(it as T)
}
複製代碼

此時埋點能夠這樣使用:

view.clickWithTrack(key) {
            ....
        }
複製代碼

或者

view.clickWithTrack(key,trackMap) {
            ....
        }
複製代碼

總結

Kotlin有不少的語法糖,使用這些語法糖能夠簡化代碼以及更優雅地實現功能。

該系列的相關文章:

使用Kotlin高效地開發Android App(五)完結篇

使用Kotlin高效地開發Android App(四)

使用Kotlin高效地開發Android App(三)

使用Kotlin高效地開發Android App(一)


Java與Android技術棧:每週更新推送原創技術文章,歡迎掃描下方的公衆號二維碼並關注,期待與您的共同成長和進步。

相關文章
相關標籤/搜索