Android@Kotlin(二) 界面构建与扩展方法

原创 2016年05月30日 17:52:21


在前面的文章中我们使用Kotlin中type-safe builder模式写了一个还算有用的v方法,它可以构建任意Android 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中,只需要:

<TextView android:maxWidith="120dp" />

本来是为了简化工作写的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 <reified TV : View> v(context: Context, init: TV.() -> Unit) : TV
inline fun <reified TV : View> 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的另外一个功能来弥补这个问题。

C#中的扩展方法学习总结

各位朋友大家好,我是秦元培,欢迎大家关注我的博客。最近偶然接触到了C#中的扩展方法,觉得这个语法特性是一个不错的特性,因此决定在这里系统地对C#中的扩展方法相关内容进行下总结和整理,因为博主觉得学习这...
  • qinyuanpei
  • qinyuanpei
  • 2015年12月07日 09:45
  • 6558

Kotlin编程之扩展方法

Kotlin编程之扩展方法
  • hexingen
  • hexingen
  • 2017年05月31日 21:59
  • 1190

Java8新特性——接口的默认方法(扩展方法)

今天是高考的日子,是大四师兄师姐答辩毕业的日子。一代又来,一代又去。好久没写博客,借此特殊日子整理一下前不久学java8新特性时写的代码,留下痕迹。(本博客的代码根据 java8新特性教程 学习整理,...
  • gdouchufu
  • gdouchufu
  • 2014年06月07日 21:13
  • 2603

c#扩展方法简介

扩展方法      如何知道的。这还得从项目的本身说起。该项目是一套的微软底层架构上搭建起来的。所有的框架以及控件的封装,数据的传递方法都是整体的框架封装好的。对常用的dropwodnlist控件...
  • han_yankun2009
  • han_yankun2009
  • 2014年05月08日 19:42
  • 3430

关于List的扩展方法

发现IEnumerable定义了很多扩展方法,这个是List的基类,所以也可以适用于List,整理如下。 1 首先要使用Linq using System.Linq 2 各函数分析如下:   ...
  • trobin
  • trobin
  • 2015年10月09日 15:35
  • 889

c# 扩展方法详解

扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的。 它们的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为前缀。 扩展方法当然不能破坏面向对象封装的概念,所以只能是访问...
  • zyh_1988
  • zyh_1988
  • 2016年04月09日 10:18
  • 1694

C# 类、结构与扩展方法

C# 类、结构与扩展方法 我并非C#大神,只是用了这么久C#,会对这门语言有着一些自己的简介。为了总结一下自己对C#的认识,也为了能够和大家一起分享学习C#的过程,我打算把我的各种各样的理解写出来。 ...
  • Froser
  • Froser
  • 2014年03月26日 16:55
  • 1658

C#扩展方法示例(this关键字)

扩展方法三要素: 静态类、静态方法、 this关键字。 本文导读:扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的。 它们的第一个参数指定该方法作用于哪个类型,并且该参数以 th...
  • Denghejing
  • Denghejing
  • 2016年09月27日 21:14
  • 2017

为JQuery扩展一个对象方法

应项目需求,对JQuery进行了一个扩展,需求如下: 项目中需要在浏览器右下角提示操作错误和系统提示内容,并有滑动移出和关闭的效果,所以自己写了一个效果还可以的弹出框来。就是给JQuery添加了一个方...
  • joyksk
  • joyksk
  • 2017年05月03日 19:42
  • 528

扩展js原生对象的正确姿势

个人不推荐修改原生对象。但是,总是不可避免的需要做一些不合理的事情。...
  • wp270280522
  • wp270280522
  • 2015年05月14日 17:54
  • 1713
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android@Kotlin(二) 界面构建与扩展方法
举报原因:
原因补充:

(最多只允许输入30个字)