东亚银行、岚图汽车带你解锁 AIGC 时代的数字化人才培养各赛道新模式! 了解详情
写点什么

实战 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:312016

评论

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

无代码是否能成为主流

间隔

QSDK/ipq5018/2T2R/Bluetooth BT5.1 supporting QCN9074/industrial wifi6 module

wallysSK

QCN9074 ipq5018

2023年上海等保测评机构名单看这里!

行云管家

等保 等级保护 等保测评 上海

PCB工程师最实用的拼版案例合集请查收!

华秋PCB

PCB PCB设计 拼版

[1.2.0新功能系列:一] Apache Doris 1.2.0 版本 Light Schema Change

张家锋

可以保存ins照片的软件~距离实现ins图片下载自由只差这一个步骤!

frank

ins图片下载

Zebec正在以流支付的方式,推动加密资产支付走上正轨

EOSdreamer111

裸辞美团花两月吃透这Java岗798道真题解析,定级阿里P7

钟奕礼

Java 程序员 java面试 java编程

一个随手就能画出小程序的工具

Towify

微信小程序 编辑器 无代码

详解视频中动作识别模型与代码实践

华为云开发者联盟

人工智能 图像识别 华为云 12 月 PK 榜

ROMA集成关键技术:增量数据集成

华为云开发者联盟

云计算 后端 华为云 12 月 PK 榜

[1.2.0新功能系列:三]Apache doris 1.2.0 Java UDF 函数开发及使用

张家锋

ubuntu18.04编译webrtc_android库

alps2006

android WebRTC

多线程安全问题原理和4种解决办法

华为云开发者联盟

开发 华为云 12 月 PK 榜

kali linux忘记密码如何重置|在kali上重置密码

Sher10ck

kali Linux

[1.2.0新功能系列:二] Apache Doris 1.2.0 JDBC外表 及 Mutil Catalog

张家锋

2022年牛客网最新版大厂Java八股文面试题总结(覆盖所有面试题考点)

架构师之道

编程 程序员 java面试

Zebec正在以流支付的方式,推动加密支付走向主流

股市老人

阿里面试败北:5种微服务注册中心如何选型?这几个维度告诉你!

架构师之道

微服务 java面试

产品负责人 VS 产品经理

ShineScrum捷行

产品经理 产品负责人

MatrixDB v4.5.0 重磅发布,全新推出 MARS2 存储引擎!

YMatrix 超融合数据库

数据库 存储引擎 超融合数据库 版本迭代 YMatrix

YOLOv5全面解析教程①:网络结构逐行代码解读

OneFlow

人工智能 深度学习 代码

能让Java开发者提高效率的10个工具

千锋IT教育

直播 | 新一代极速云原生湖仓的技术内核,StarRocks PMC 今天下午为你揭秘!

StarRocks

#数据库

软件测试丨只需学会Docker,环境问题再也不是测开路上的「坑」

测试人

Docker 容器 软件测试 自动化测试 测试开发

面对复杂中国式报表,哪款BI商业智能工具适合你?

小偏执o

StoneDB荣获"2022年度优秀开源技术团队" : 决心做好下一代MySQL高性能分析加速器

StoneDB

MySQL 开源 HTAP 数据库· StoneDB

网站等保三级怎么做?手续是怎样的?

行云管家

等保 等级保护 过等保 等保三级

Chaos 测试下的若干 NebulaGraph Raft 问题分析

NebulaGraph

分布式 分布式系统 raft 分布式图数据库

超赞!阿里大牛熬夜纯手写的微服务架构笔记,肝完面试犹如开挂

钟奕礼

Java 程序员 java面试 java编程

有奖征文 | “用 TDengine,写 TDengine”,万元大奖等你来瓜分!

TDengine

tdengine TDengine征文

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