使用Kotlin高效地開(kāi)發(fā)Android App(二)

一.apply 函數(shù) 和 run 函數(shù)

1.1 apply函數(shù)

apply函數(shù)是指在函數(shù)塊內(nèi)可以通過(guò) this 指代該對(duì)象,返回值為該對(duì)象自己。在鏈?zhǔn)秸{(diào)用中,可以考慮使用它來(lái)不破壞鏈?zhǔn)健?/p>

/**
 * 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
}

舉個(gè)例子:

/**
 * 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)
    }
}

執(zhí)行結(jié)果:

Hello World
Hello

第一個(gè)字符串是在閉包中打印的,第二個(gè)字符串是result的結(jié)果,它仍然是“Hello”。

1.2 run函數(shù)

run函數(shù)類似于apply函數(shù),但是run函數(shù)返回的是最后一行的值。

/**
 * 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()
}

舉個(gè)例子:

/**
 * 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)
    }
}

執(zhí)行結(jié)果:

Hello World
Hello World

第一個(gè)字符串是在閉包中打印的,第二個(gè)字符串是result的結(jié)果,它返回的是閉包中最后一行的值,所以也打印“Hello World”。

1.3 項(xiàng)目中的使用

在App的反饋?lái)?yè)面中,需要輸入郵箱、主題、內(nèi)容才能完成反饋按鈕的提交。

最初的寫(xiě)法是這樣:

        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函數(shù)

       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
            }
        }

感覺(jué)不夠cool,可以結(jié)合run和apply函數(shù)一起使用

        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有點(diǎn)類似于Scala的case class。

以下的Java Bean代碼

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

    public String userName;
    public String password;
}

等價(jià)于

data class User (var userName: String? = null,var password: String? = null)

可以看到采用data class能夠簡(jiǎn)化Java Bean類。我們的App采用了MVVM的架構(gòu),因此對(duì)應(yīng)Model類全部使用data class。

三.無(wú)需使用findViewById 或者 butterknife

使用Kotlin Android Extensions插件即可實(shí)現(xiàn)該功能,它是Kotlin 插件的組成之一,無(wú)需再單獨(dú)安裝插件。

我們?cè)诟鱾€(gè)modules的build.gradle中添加該插件,即可使用。

apply plugin: 'kotlin-android-extensions'

布局文件中的id,可以直接在代碼中使用。
首先,按照import kotlinx.android.synthetic.main.布局文件名*的方式導(dǎo)入。
例如MainActivity,它的布局文件是activity_main.xml
則按照如下的方式進(jìn)行import

import kotlinx.android.synthetic.main.activity_main.*

那么activity_main.xml中控件的id,可以直接在MainActivity中使用,無(wú)需使用findViewById 或者 butterknife。是不是特別方便?

四.點(diǎn)擊事件的埋點(diǎn)處理

App的埋點(diǎn),使用自己家的產(chǎn)品--魔窗的sdk來(lái)做事件的埋點(diǎn)。

如果使用Java來(lái)開(kāi)發(fā)App,可以使用AOP來(lái)實(shí)現(xiàn)埋點(diǎn)。由于我們的App采用Kotlin編寫(xiě),Kotlin可以將事件的點(diǎn)擊簡(jiǎn)化成如下的形式

        view.setOnClickListener {
             ....
        }

這種簡(jiǎn)化了的lambda表達(dá)式,所以我還是老老實(shí)實(shí)的使用傳統(tǒng)方式進(jìn)行埋點(diǎn)。

使用Kotlin的通常做法:

        view.setOnClickListener {

             TrackAgent.currentEvent().customEvent(eventName)
             ....
        }

或者

        view.setOnClickListener {

             TrackAgent.currentEvent().customEvent(eventName, trackMap)
             ....
        }

后來(lái),我寫(xiě)了一個(gè)View的擴(kuò)展函數(shù)click,后來(lái)經(jīng)過(guò)同事的優(yōu)化

目前,已經(jīng)將該擴(kuò)展函數(shù)放入我的Kolin的工具類庫(kù) https://github.com/fengzhizi715/SAF-Kotlin-Utils

此時(shí),埋點(diǎn)的代碼變成這樣

        view.click {

             TrackAgent.currentEvent().customEvent(eventName)
             ....
        }

或者

        view.click {

             TrackAgent.currentEvent().customEvent(eventName, trackMap)
             ....
        }

進(jìn)一步的優(yōu)化處理,對(duì)于View增加擴(kuò)展函數(shù)clickWithTrack專門(mén)用于埋點(diǎn)的點(diǎn)擊事件。

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 <描述當(dāng)前版本功能>
 */

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)
}

此時(shí)埋點(diǎn)可以這樣使用:

        view.clickWithTrack(key) {
            ....
        }

或者

        view.clickWithTrack(key,trackMap) {
            ....
        }

喜歡點(diǎn)擊+關(guān)注哦

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容