2天时间,聊今年最热的 Agent、上下文工程、AI 产品创新等话题。2025 年最后一场~ 了解详情
写点什么

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

评论

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

如何系统化搭建超智融合算力架构 | 龙蜥大讲堂

OpenAnolis小助手

操作系统 龙蜥社区 龙蜥大讲堂 超智融合算力

鸿蒙智能催办实战:Push Kit实现文档流程零延迟推进

huafushutong

谷云科技 iPaaS 成功入选Gartner 2025 API 管理市场指南

谷云科技RestCloud

AI API网关 集成平台 ipaas API 管理

鸿蒙地理围栏实战:Location Kit实现智能文档地理围栏

huafushutong

HarmonyOS SDK应用服务

鸿蒙智能文档治理实战:File Manager Service Kit实现企业级文件管理

huafushutong

HarmonyOS SDK应用服务

IDM使用教程之如何下载网页中的视频资源,电脑网页上的视频怎么保存到本地

阿拉灯神丁

视频下载器 内容嗅探 IDM 网页视频下载工具 如何下载网页视频

超实用!SpringAI提示词的4种神级用法

电子尖叫食人鱼

数据库 前端

鸿蒙PDF引擎实战:PDF Kit实现亿级文档极速渲染

huafushutong

鸿蒙文档预检实战:Preview Kit实现合同风险智能预判

huafushutong

编程简单了,部署依旧很难|Karpathy 演讲的 5 点解读

阿里巴巴云原生

阿里云 云原生

语音生成+情感复刻,Cosyvoice2.0 极简云端部署

阿里巴巴云原生

阿里云 Serverless 云原生 函数计算

鸿蒙实时协同实战:Live View Kit打造沉浸式文档批注系统

huafushutong

HarmonyOS SDK应用服务

KubeEdge 1.21.0版本发布!节点任务框架全面升级!

华为云原生团队

云计算 容器 云原生

通义灵码用户说 | 编程智能体+MCP加持,秒查附近蜜雪冰城

阿里巴巴云原生

通义灵码

鸿蒙健康守护实战:Health Service Kit实现智能文档工作监护

huafushutong

HarmonyOS SDK应用服务

鸿蒙地理可视化实战:Map Kit实现智能文档位置洞察

huafushutong

HarmonyOS SDK应用服务

枫清科技受邀参加2025数据智能大会

Fabarta

人工智能 科技 数据智能大会

什么是OA 系统?OA 系统要具备什么样的功能?

积木链小链

数字化转型 智能制造 OA系统

鸿蒙安全共享实战:Share Kit实现文档一键安全分发

huafushutong

什么是链游,链游系统开发价格以及方案

区块链软件开发推广运营

交易所开发 dapp开发 链游开发 公链开发 代币开发

鸿蒙互动化实战:Game Service Kit重塑文档学习体验

huafushutong

HarmonyOS NEXT

鸿蒙文档解析实战:Reader Kit实现合同智能解构

huafushutong

鸿蒙场景融合实战:Scenario Fusion Kit实现跨文档智能关联

huafushutong

通义灵码用户说 | 编程智能体+MCP加持,秒查附近蜜雪冰城

阿里云云效

通义灵码 Qwen

鸿蒙智能提醒实战:Notification Kit实现文档关键节点零遗漏

huafushutong

鸿蒙数字凭证实战:Wallet Kit实现文档电子凭证管理

huafushutong

鸿蒙商业化实战:IAP Kit构建安全文档付费体系

huafushutong

HarmonyOS SDK应用服务

ITSM 与自动化联动,让运维工作轻松翻倍

智象科技

自动化 ITSM 自动化运维 一体化运维

DApp开发:下一代互联网应用的底层逻辑与技术实践

区块链软件开发推广运营

交易所开发 dapp开发 链游开发 公链开发 代币开发

鸿蒙天气服务实战:Weather Service Kit实现文档工作流智能优化

huafushutong

鸿蒙支付安全实战:Payment Kit构建文档服务可信交易体系

huafushutong

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