【AICon】探索八个行业创新案例,教你在教育、金融、医疗、法律等领域实践大模型技术! >>> 了解详情
写点什么

实战 Kotlin@Andorid(二):界面构建与扩展方法

  • 2016-05-02
  • 本文字数:2491 字

    阅读完需:约 8 分钟

版权声明

原作者:Doug Stevenson

译者:程大治

本文由原作者授权翻译并发布,首发于移动开发前线公众号,未经允许禁止转载。

这是实战 Kotlin@Android 的第二部分,如果你还没读过第一部分,建议先阅读第一部分实战Kotlin@Android(一):项目配置和语言转换

在前面的文章中我们使用Kotlin 中type-safe builder 模式写了一个还算有用的v 方法,它可以构建任意Android View 实例。

我们可以在其他Kotlin 代码中调用这个方法来创建并初始化任何类型的View:

这真的实用吗?

现在我们要创建一个很简单的layout,它包含两个TextView。在XML 可以这样表示:

我们可怜的v 方法不能一下子创建这么多,不过只需借助一点帮助。我们需要再写一个能够将View 添加至父View(LinearLayout, RelativeLayout)的方法。我们现在写一个新的v 方法。

这个方法与原本的v 方法几乎一摸一样,区别只在第一个参数上:不再是Context, 变成了ViewGroup 类型。这个新的v 方法需要持有父ViewGroup,以便将新创建的View 对象在初始化并返回之前添加进其中。而新的v 方法又能通过ViewGroup 来获取Context 以初始化View,这样就不用再传入Context 对象了。

现在我们看一下新的v 方法如何与旧的协作来构建上述View 层级。

这里Kotlin 代码会像XML 一样嵌套,非常好看。

在上一部分中说过,Kotlin lambda with receiver 可以在lambda 内部以this 关键字引用receiver 对象。在上面的例子中,在外部v 的lambda 中receiver 是LinearLayout,它作为第一个参数被传入了两个内部v 方法(刚写的v 方法)。因为LinearLayout 是ViewGroup 的子类,Kotlin 知道我们在调用新写的v 方法,因为旧的需要传入Context。

通过这两个兄弟v 方法我们可以动态地、精确地创建嵌套View,其中的ViewGroup 和View 的具体类型均无限制。现在我们已经可以发现这种表述性的创建方式与XML 有些相似,而在后续的文章中,我们也将发现Kotlin 的速度要快一些。

提升空间

Kotlin 的 type-safe builder 模式起了很大的作用,但是在很多时候,Kotlin 还是比 XML 复杂不少。比如在 Kotlin 中当我们想设置一个 TextView 的 maxWidth 属性为 120dp 时:

而在 XML 中,只需要:

本来是为了简化工作写的 v 方法一下变麻烦了。

这里需要将 dp 转化为 px 的简便方法

我在这里想要一个方法可以将 dp 转化为像素,然后上面的代码最好能长这样:

这里的方法可以接收一个以 dp 为单位的值,然后返回当前设备下转化成像素的值。不过为什么要叫这个方法 dp_i 而不是 dp 呢?在 Android 中有时会返回 float 而有时会返回 int,我也不想再自己进行转换,所以就给两种返回类型都写一个方法:"dp_i"和“dp_f”。

但在这里仍有问题。如果你看一下刚才很丑的那段代码,会发现计算像素值时需要 Context 对象。我可不想每次调用 dp_i 方法都传入 Context 作为参数,所以在这里要用到 Kotlin 的另一个技能:extension functions 扩展方法。让我们直接看一下扩展方法长什么样:

扩展方法如何工作?

你可能注意到的第一个点就是方法的前缀。你可能本以为第一个方法应该是 dp_f,结果是 View.dp_f。这是 Kotlin 中针对扩展方法的一个特殊语法。这里将一个类名和一个方法名以点连接,而意思就是告诉 Kotlin 我们要给 View 类添加两个新方法"dp_i",“dp_f”。这样使用扩展方法有几点好处:

第一,在扩展方法内作为类的成员可以访问其成员变量和方法(只有 public 和 internal)。也就是说 dp_f 可以通过 View 内部的 context 属性来访问其 Context 引用。现在我们不需要将 Context 作为参数传入了,因为它隐含在 View 中。

第二,在导入了 (import) 这些扩展方法的代码段中可以像调用一个对象的普通方法一样调用其扩展方法。在这里,在 v 方法的 lambda with receiver 代码块中可以通过 receiver View 对象直接调用这些方法,像这样:maxWidth = dp_i(120),Kotlin 会识别出需要调用 View 类型的 receiver 对象的 dp_i 方法。

值得注意的一点是,Kotlin 在声明扩展方法时,不会修改其 class。所以在这里,View 类的其他方法不能访问扩展方法,因为扩展方法不是真正意义上的成员。

现在我们就有将 dp 转成 px 的简便方法了。

扩展方法还有其他的用处。现在我们已经看到通过扩展方法可以简化一些棘手的代码,我们利用这一点继续简化 v 方法。

现在我们有两个 v 方法,第一个用于构建根元素,接收 Context,第二个用于创建嵌套于父 View 中的子 View。

inline fun v(context: Context, init: TV.() -> Unit) : TV
inline fun v(parent: ViewGroup, init: TV.() -> Unit) : TV

如果我们不需要传入 Context 或是 ViewGroup 作为参数,岂不是很好?通过扩展方法,我们就像刚才避免将 Context 传入 dp_f 一样重构这段代码。下面使用扩展方法重新实现两个 v 方法,注释是两个方法原本的声明。

你可以看到我们去掉了两个方法的第一个参数(Context 和 ViewGroup),并通过所继承的类来获取所需实例的引用。现在这两个方法都只有一个参数:用于修改 View 的 lambda with recceiver。

修改了方法后,如果我们在 Activity(Context 子类)中写代码,那就可以将 v 添加做 Activity 对象的成员。这样我们就可以以这样更简单的方式构建嵌套 View。

这里调用 v 方法根本不像是在调用方法,因为我们不需要圆括号。在第一部分中我说过,如果方法的最后一个参数是 lambda,那就可以放在圆括号后,而在这里,只有一个参数,根本就不用写圆括号。

Kotlin 中的扩展方法帮我们在代码中很简明易懂地创建构建 View 层级。不过还是有其他问题需要注意。比如我们想设置 TextView 的左内边距为 16dp。

在这里调用 setPadding() 方法与直接修改属性放在一起真是挺丑的,之所以有这样的情况发生,是因为 setPadding() 方法有多个参数,并不是一个 JavaBean 风格的 Setter 方法。所以,Kotlin 不能为其制定一个虚拟属性。不用怕,我会在后续文章中通过 Kotlin 的另外一个功能来弥补这个问题。


感谢徐川对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-05-02 17:312020

评论

发布
暂无评论
发现更多内容

Appriority for mac(CPU监测优化工具)v2024.1激活版

影影绰绰一往直前

XML文档节点导航与选择指南

小万哥

xml 程序人生 编程语言 软件工程 前端开发

Ascend C 自定义算子 Kernel Launch调用入门

华为云开发者联盟

人工智能 华为云 华为云开发者联盟 企业号2024年4月PK榜 自定义算子开发

IT行业网络安全守护者-行云管家云堡垒机

行云管家

网络安全 IT 数据安全 堡垒机

随易周刊第002期 - 四象

前端之虎陈随易

程序员 前端 Web 周刊 技术周刊

✅基于TTL 解决线程池中 ThreadLocal 线程无法共享的问题

派大星

ThreadLocal Java 面试题 互联网大厂面试

捷途山海T2探秘武夷山,这款旅行越野超混SUV直接拉满期待值

Geek_2d6073

华为云GeminiDB,广告RTA的“登云梯”

华为云开发者联盟

数据库 华为云 华为云开发者联盟 华为云GeminiDB 企业号2024年4月PK榜

Linux虚拟网络设备全景解析:定义、工作模式与实践应用

GousterCloud

Linux Kenel 设备

虚拟网络设备性能优化

GousterCloud

Linux Kenel 虚拟网卡

透明LED屏幕如何设计效果更好?

Dylan

技术 设计 系统 LED LED显示屏

5.7打补丁—编译和官方一致的Linux_Generic包

GreatSQL

2024上海国际半导体产业展览会

AIOTE智博会

半导体展 半导体展会 半导体展览会 半导体博览会

虚拟网络设备的网络隔离机制:原理、意义与应用场景深度分析

GousterCloud

Linux Kenel 网卡

虚拟网络设备与网络安全:深入分析与实践应用

GousterCloud

Linux Kenel 虚拟网卡

心态崩了,约了半个月,就只有3个面试!

王中阳Go

微服务 并发编程 面试题 面经 大厂面经

虚拟网络设备与Linux网络协议栈

GousterCloud

Linux Kenel

深入了解商品口碑:淘宝评论API助力消费者明智选择

技术冰糖葫芦

API Explorer API 文档 pinduoduo API

NineData创始人&CEO叶正盛受邀参加『数据技术嘉年华』的技术大会

NineData

数据库 数据复制 实践 NineData 技术原理

探索未来产业:新技术、新商业、新趋势

天津汇柏科技有限公司

未来产业

第46期 | GPTSecurity周报

云起无垠

Databend 开源周报第 139 期

Databend

面试官:Redis如何实现延迟任务?

王磊

Java 面试题

Linux虚拟网络设备:底层原理与性能优化深度解析

GousterCloud

Linux Kenel 设备驱动

打造银行核心全栈国产化新模式,第三届数字金融技术创新与应用研讨会成功举办

先锋IT

阿里云佘俊泉:边缘云场景的探索与机遇

EdgeMaster

异构计算 边缘云 云网融合

浅析如何加速商业业务实时化

百度Geek说

流式计算 企业号 4 月 PK 榜

再添一喜!行云管家堡垒机与人大金仓完成兼容性认证

行云管家

数据库 人大金仓 兼容 兼容认证

虚拟网络设备的真正使命:实现有控制的通信

GousterCloud

Linux Kenel

一键生成视频!用 PAI-EAS 部署 AI 视频生成模型 SVD 工作流

阿里云大数据AI技术

人工智能 阿里云 AI视频

一次对Redis内存调整的过程

麦兜

redis

实战Kotlin@Andorid(二):界面构建与扩展方法_移动_Doug Stevenson_InfoQ精选文章