写点什么

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

评论

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

[Go WebSocket] 为什么我选用Go重构Python版本的WebSocket服务?

HullQin

Go golang 后端 websocket 8月月更

区块链交易隐私如何保证?华为零知识证明技术实战解析

创意时空

每日一R「21」Unsafe Rust

Samson

学习笔记 8月月更 ​Rust

自然语言处理--神经网络的复习

IT蜗壳-Tango

自然语言处理 nlp 9月月更

构建万物可信的基石:解密区块链跨链技术

创意时空

【编程实践】认识爬虫并手把手带手实现新闻网站的爬取

迷彩

记录 Python爬虫 8月月更 网络爬虫

IDEA配置tomcat

楠羽

#开源

阿里云高性能计算负责人何万青:阿里云大计算加速HPC与AI融合

阿里云弹性计算

AI HPC 高性能计算 无影云电脑 计算巢

Java进阶(一)内存解析

No Silver Bullet

Java 9月月更 内存解析

低代码是什么?国内排名前 5 的低代码开发平台对比

蒋川

低代码 开发工具 开发平台

【JVM】HotspotJVM 分代回收机制

小明Java问道之路

8月月更

一起学习设计模式:责任链模式

宇宙之一粟

设计模式 8月月更

玩转KubeEdge保姆级攻略

乌龟哥哥

8月月更

iofod导入任意前端资产,以 Element UI 为例

iofod jude

小程序 前端 低代码 网页

Network源码接口分析

长安链

rocksdb和innodb的一些区别

趁早

【算法实践】一天路走到黑--手把手带你实现坚持不懈的线性查找

迷彩

Python 数据结构 算法实践 8月月更 线性查找

Go 代码城市上云——KusionStack 实践

SOFAStack

开源

技术解析+代码实战,带你入门华为云政务区块链平台

创意时空

电商订单全流程可观测性最佳实践

观测云

金融科技创新者的困境

木风

金融科技 数字化转型 科技创新

[教你做小游戏] 展示斗地主扑克牌,支持按出牌规则排序!支持按大小排序!

HullQin

CSS JavaScript html 前端 9月月更

影视动漫制作为什么要选择云渲染农场?

Finovy Cloud

计算器 云渲染 影视渲染

创投基金黑钻资本Black3Lab Capital主投互联网3.0

股市老人

【JVM】HotspotJVM精通垃圾回收器原理

小明Java问道之路

8月月更

微服务网关Gateway实践总结

Java 架构

艺术收藏NFT系统开发:NFT功能搭建

开源直播系统源码

数字藏品 数字藏品系统软件开发 数字藏品开发

这些智能合约漏洞,可能会影响你的账户安全!

创意时空

C/CPP基础练习题多维数组,矩阵转置,杨辉三角详解

CtrlX

c c++ 基础 8月月更

基于Vue3常用代码块

青柚1943

typescript Vue3 Element Plus Pinia sortablejs

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