使用Kotlin高效地开发Android App(二)

640?wx_fmt=jpeg


上一篇文章介绍了项目中所使用的Kotlin特性,本文继续整理当前项目所用到的特性。

一.apply 函数 和 run 函数

with、apply、run函数都是Kotlin标准库中的函数。with在第一篇文章中已经介绍过。

1.1 apply函数

apply函数是指在函数块内可以通过 this 指代该对象,返回值为该对象自己。在链式调用中,可以考虑使用它来不破坏链式。

 
 
  1. /**

  2. * Calls the specified function [block] with `this` value as its receiver and returns `this` value.

  3. */

  4. @kotlin.internal.InlineOnly

  5. public inline fun <T> T.apply(block: T.() -> Unit): T {

  6.    contract {

  7.        callsInPlace(block, InvocationKind.EXACTLY_ONCE)

  8.    }

  9.    block()

  10.    return this

  11. }

举个例子:

 
 
  1. /**

  2. * Created by tony on 2018/4/26.

  3. */

  4. object Test {

  5.    @JvmStatic

  6.    fun main(args: Array<String>) {

  7.        val result ="Hello".apply {

  8.            println(this+" World")

  9.            this+" World"

  10.        }

  11.        println(result)

  12.    }

  13. }

执行结果:

 
 
  1. Hello World

  2. Hello

第一个字符串是在闭包中打印的,第二个字符串是result的结果,它仍然是“Hello”。

1.2 run函数

run函数类似于apply函数,但是run函数返回的是最后一行的值。

 
 
  1. /**

  2. * Calls the specified function [block] with `this` value as its receiver and returns its result.

  3. */

  4. @kotlin.internal.InlineOnly

  5. public inline fun <T, R> T.run(block: T.() -> R): R {

  6.    contract {

  7.        callsInPlace(block, InvocationKind.EXACTLY_ONCE)

  8.    }

  9.    return block()

  10. }

举个例子:

 
 
  1. /**

  2. * Created by tony on 2018/4/26.

  3. */

  4. object Test {

  5.    @JvmStatic

  6.    fun main(args: Array<String>) {

  7.        val result ="Hello".run {

  8.            println(this+" World")

  9.            this + " World"

  10.        }

  11.        println(result)

  12.    }

  13. }

执行结果:

 
 
  1. Hello World

  2. Hello World

第一个字符串是在闭包中打印的,第二个字符串是result的结果,它返回的是闭包中最后一行的值,所以也打印“Hello World”。

1.3 项目中的使用

在App的反馈页面中,需要输入邮箱、主题、内容才能完成反馈按钮的提交。

最初的写法是这样:

 
 
  1.        if (viewModel.email.value!!.isEmpty()) {

  2.            toast(resources.getString(R.string.you_have_not_completed_the_email_address)).show()

  3.            return@onClickRight

  4.        }

  5.        if (!Util.checkEmail(viewModel.email.value!!)) {

  6.            toast(resources.getString(R.string.the_email_format_you_have_filled_is_incorrect)).show()

  7.            return@onClickRight

  8.        }

  9.        if (viewModel.subject.value!!.isEmpty()) {

  10.            toast(resources.getString(R.string.you_have_not_completed_the_feedback_subject)).show()

  11.            return@onClickRight

  12.        }

  13.        if (viewModel.content.value!!.isEmpty()) {

  14.            toast(resources.getString(R.string.you_have_not_completed_the_details)).show()

  15.            return@onClickRight

  16.        }

修改成只使用apply函数

 
 
  1.       viewModel.apply {

  2.            if (email.value!!.isEmpty()) {

  3.                toast(resources.getString(R.string.you_have_not_completed_the_email_address)).show()

  4.                return@onClickRight

  5.            }

  6.            if (!Util.checkEmail(email.value!!)) {

  7.                toast(resources.getString(R.string.the_email_format_you_have_filled_is_incorrect)).show()

  8.                return@onClickRight

  9.            }

  10.            if (subject.value!!.isEmpty()) {

  11.                toast(resources.getString(R.string.you_have_not_completed_the_feedback_subject)).show()

  12.                return@onClickRight

  13.            }

  14.            if (content.value!!.isEmpty()) {

  15.                toast(resources.getString(R.string.you_have_not_completed_the_details)).show()

  16.                return@onClickRight

  17.            }

  18.        }

感觉不够cool,可以结合run和apply函数一起使用

 
 
  1.        viewModel.email.run {

  2.            if (value!!.isEmpty()) {

  3.                toast(resources.getString(R.string.you_have_not_completed_the_email_address)).show()

  4.                return@onClickRight

  5.            }

  6.            if (!Util.checkEmail(value!!)) {

  7.                toast(resources.getString(R.string.the_email_format_you_have_filled_is_incorrect)).show()

  8.                return@onClickRight

  9.            }

  10.            viewModel

  11.        }.subject.run {

  12.            if (viewModel.subject.value!!.isEmpty()) {

  13.                toast(resources.getString(R.string.you_have_not_completed_the_feedback_subject)).show()

  14.                return@onClickRight

  15.            }

  16.            viewModel

  17.        }.content.apply {

  18.            if (value!!.isEmpty()) {

  19.                toast(resources.getString(R.string.you_have_not_completed_the_details)).show()

  20.                return@onClickRight

  21.            }

  22.        }

二.data class

Kotlin的data class有点类似于Scala的case class。

以下的Java Bean代码

 
 
  1. /**

  2. * Created by tony on 2018/4/27.

  3. */

  4. public class User {

  5.    public String userName;

  6.    public String password;

  7. }

等价于

 
 
  1. 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中添加该插件,即可使用。

 
 
  1. apply plugin: 'kotlin-android-extensions'

布局文件中的id,可以直接在代码中使用。 首先,按照import kotlinx.android.synthetic.main.布局文件名*的方式导入。

例如MainActivity,它的布局文件是activity_main.xml 则按照如下的方式进行import

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

那么activity_main.xml中控件的id,可以直接在MainActivity中使用,无需使用findViewById 或者 butterknife。是不是特别方便?

四.点击事件的埋点处理

App的埋点,使用自己家的产品--魔窗的sdk来做事件的埋点。

如果使用Java来开发App,可以使用AOP来实现埋点。由于我们的App采用Kotlin编写,Kotlin可以将事件的点击简化成如下的形式

 
 
  1.        view.setOnClickListener {

  2.             ....

  3.        }

这种简化了的lambda表达式,所以我还是老老实实的使用传统方式进行埋点。

使用Kotlin的通常做法:

 
 
  1.        view.setOnClickListener {

  2.             TrackAgent.currentEvent().customEvent(eventName

  3.             ....

  4.        }

或者

 
 
  1.        view.setOnClickListener {

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

  3.             ....

  4.        }

后来,我写了一个View的扩展函数click,后来经过同事的优化。可以查看简书的这篇文章 <<利用扩展函数优雅的实现“防止重复点击”>>(https://www.jianshu.com/p/7118226ecba9)

目前,已经将该扩展函数放入我的Kolin的工具类库 https://github.com/fengzhizi715/SAF-Kotlin-Utils

此时,埋点的代码变成这样

 
 
  1.        view.click {

  2.             TrackAgent.currentEvent().customEvent(eventName

  3.             ....

  4.        }

或者

 
 
  1.        view.click {

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

  3.             ....

  4.        }

进一步的优化处理,对于View增加扩展函数clickWithTrack专门用于埋点的点击事件。

 
 
  1. package cn.magicwindow.core.ext

  2. import android.view.View

  3. import cn.magicwindow.TrackAgent

  4. import com.safframework.ext.clickWithTrigger

  5. /**

  6. *

  7. * @FileName:

  8. *          cn.magicwindow.core.ext.ViewExt.java

  9. * @author: Tony Shen

  10. * @date: 2018-04-24 17:17

  11. * @version V1.0 <描述当前版本功能>

  12. */

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

  14.    TrackAgent.currentEvent().customEvent(eventName)

  15.    block(it as T)

  16. }

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

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

  19.    block(it as T)

  20. }

此时埋点可以这样使用:

 
 
  1.        view.clickWithTrack(key) {

  2.            ....

  3.        }

或者

 
 
  1.        view.clickWithTrack(key,trackMap) {

  2.            ....

  3.        }

总结

Kotlin有很多的语法糖,使用这些语法糖可以简化代码以及更优雅地实现功能。

先前的文章:

使用Kotlin高效地开发Android App(一)


关注【Java与Android技术栈】

更多精彩内容请关注扫码

640?wx_fmt=jpeg


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值