}
//专门定义了一个接口来传递这个点击事件
公共接口OnClickListener {
无效onClick(查看v);
}
//监听鼠标点击事件
公共无效setOnContextClickListener(OnContextClickListener l){
getListenerInfo().mOnContextClickListener=l;
}
//专门定义了一个接口来传递这个鼠标点击事件
公共接口OnContextClickListener {
Boolean onContextClick(View v);
}
要在Android 上设置单击和鼠标单击事件,请编写:
//设置手指点击事件
image.setOnClickListener(new View.OnClickListener() {
@覆盖
公共无效onClick(查看v){
转到预览();
}
});
//设置鼠标点击事件
image.setOnContextClickListener(new View.OnContextClickListener() {
@覆盖
公共无效onContextClick(查看v){
转到预览();
}
});
您是否认为这段代码太冗长?
现在,假设我们是语言设计者,让我们首先看看上面代码中的问题。
定义者:每次增加一个方法,我们都要增加一个新的接口:OnClickListener、OnContextClickListener 调用者:我们要写很多匿名内部类,冗余、笨重、不集中。
如果你仔细看上面的代码,你会发现开发人员实际上只关心一行代码。
转到预览();
这是提取核心逻辑的最简单方法。
image.setOnClickListener { gotoPreview() }
image.setOnContextClickListener { gotoPreview() }
Kotlin 语言的设计者做了什么?
将接口定义替换为函数类型,并使用lambda 表达式作为函数参数
上面View.java 的Kotlin 等效项是:
//查看.kt
var mOnClickListener: ((视图) – 单位)=null
var mOnContextClickListener:((视图)-单位)?
fun setOnClickListener(l: (视图) – 单位) {
mOnClickListener=l;
}
有趣的setOnContextClickListener(l: (视图) – 单位) {
mOnContextClickListener=l;
}
上述方法具有以下优点:
Definer:减少两个接口类的定义。 来电者:代码更简洁了。
细心的朋友可能已经发现了问题所在。 Android 不提供View.java 的Kotlin 实现。为什么在我们的演示中使用Lambda 来简化事件监控?
//实际开发中经常使用这种简化的方法
setOnClickListener { gotoPreview() }
原因如下: OnClickListener满足SAM转换的要求,允许编译器自动运行转换层并使用lambda表达式简化函数调用。
那么SAM到底是什么?
2-2 SAM 转换(Single Abstract Method Conversions)
SAM(Single Abstract Method),顾名思义,是只有一个抽象方法的类或接口,但在Kotlin和Java8中,SAM代表只有一个抽象方法的接口。对于满足SAM要求的接口,编译器可以进行SAM转换。这允许您使用lambda 表达式来省略接口类中的参数。
注意:Java8 的SAM 有一个独特的名称:FunctionalInterface。
功能接口的限制是:所有这些都是必需的。
它必须是一个接口,而不是一个抽象类。一个接口只有一个抽象方法。默认情况下,可以实现多种方法。
因此,对于View.java,即使这是Java 代码,Kotlin 编译器也知道其参数OnClickListener 符合SAM 转换的条件,因此它会自动执行以下转换:
转换前:
公共无效setOnClickListener(OnClickListener l)
转换后:
fun setOnClickListener(l: (视图) – 单位)
//实际上这是:
fun setOnClickListener(l: ((View!) – 单位)?)
((View!) – Unit)? 表示该参数可能为空。
2-3 Lambda 表达式引发的8种写法
当lambda表达式用作函数参数时,有时可以省略,使代码看起来更简洁。然而,大多数初学者会觉得很困惑,因为相同的代码可以用8 种不同的方式编写。
理解lambda 表达式的省略逻辑实际上非常简单。这意味着要写更多。
大家好,请按照下面的流程,跟我一起写吧。
2-3-1 第1种写法
这是原始代码,基本上使用object 关键字定义一个匿名内部类。
image.setOnClickListener(object: View.OnClickListener {
覆盖fun onClick(v: View?) {
转到预览(v)
}
})
2-3-2 第2种写法
由于删除object 关键字会生成Lambda 表达式,因此您还必须删除其中的覆盖方法。
image.setOnClickListener(View.OnClickListener { view: 视图?-
转到预览(显示)
})
上面的View.OnClickListener是这样调用的: SAM Constructor—— SAM 构造函数由编译器生成。 Kotlin 允许您以这种方式定义lambda 表达式。
思考题:
目前,View.OnClickListener {}在语义上是一个Lambda表达式,但在语法上它仍然是一个匿名内部类。这是对的?
2-3-3 第3种写法
Kotlin 中的Lambda 表达式不需要SAM 构造函数,因此您也可以将其删除。
image.setOnClickListener({ view: 显示? –
转到预览(显示)
})
2-3-4 第4种写法
Kotlin 支持类型推断,因此您可以删除View?
image.setOnClickListener({ 查看-
转到预览(显示)
})
2-3-5 第5种写法
如果您的Kotlin lambda 表达式只有一个参数,您可以按原样编写。
image.setOnClickListener({ 它-
转到预览(它)
})
2-3-6 第6种写法
对于Kotlin Lambda 可以省略。
image.setOnClickListener({
转到预览(它)
})
2-3-7 第7种写法
如果使用Kotlin Lambda 作为函数的最后一个参数,则可以将Lambda 移至外部。
image.setOnClickListener() {
转到预览(它)
}
2-3-8 第8种写法
如果Kotlin 只有一个Lambda 作为函数参数,则() 可以省略。
image.setOnClickListener {
转到预览(它)
}
一旦你遵循这个过程并在IDE 中编写几次,它就会成为你的第二天性。你必须写它,即使你读了这篇文章你也不会记住它。
2-4 函数类型,高阶函数,Lambda表达式三者之间的关系
抽象函数参数的类型及其返回值就可以得出函数的类型。 (View) – Unit 表示参数类型为View,返回类型为Unit。如果函数的参数或返回类型是函数类型,则该函数是高阶函数。显然,我们刚刚创建了一个高阶函数,但它相对简单。 Lambda 是函数的缩写
理解图:函数类型、高阶函数和lambda 表达式之间的关系:
我们回过头来看看官方文档中提供的示例。
有趣的T,R集合.fold(
初始: R,
组合:(acc: R,nextElement: T)-R
):R{
var Accumulator: R=初始值
for(其中的元素: T){
累加器=join(累加器, 元素)
}
返回累加器
}
看一下这个函数类型:(acc: R, nextElement: T) – R,你能快速理解这个函数有两个参数吗?第一个参数的类型为R,第二个参数的类型为T,函数的返回类型为R。
3. 带接收者(Receiver)的函数类型:A.(B,C) – D
说实话,这个名字对于初学者来说不太友好:Function Types With Receiver 里面的所有单词(单词)我们都认识,但是这么多的信息对于初学者来说确实很难理解它的本质是什么。
仍然有一个不可避免的问题:“为什么?”
3-1 为什么要引入:带接收者的函数类型?
在上一章中,我写了使用apply 来简化逻辑:
修复前:
如果(用户!=空){
……
用户名.文本=用户名
网站.文本=用户.博客
image.setOnClickListener { gotoImagePreviewActivity(用户) }
}
更改后:
用户?应用{
……
用户名.文本=姓名
网站.文本=博客
image.setOnClickListener { gotoImagePreviewActivity(this) }
}
请问大家一个问题:这个apply方法应该如何实现呢?
上面的语句实际上是一个简化的Lambda表达式。让我们反转一下,看看简化前是什么样子。
//apply 必须是一个函数,因此() 存在但被省略
用户?apply() {
……
}
//Lambda 必须在() 内
用户?apply({ … })
//gotoImagePreviewActivity(this)中的this代表用户
//所以user必须是apply函数的参数,参数名称为:
用户?apply({ this: 用户- … })
那么问题就变得很清楚了,apply实际上接收到了一个Lambda表达式:{ this: User – . }。让我们来实现这个apply方法。
fun User.apply(block: (self: User) – 单位): User{
块(自身)
返回这个
}
用户?apply { self: 用户-
……
用户名.文本=self.名字
网站.文本=self.博客
image.setOnClickListener { gotoImagePreviewActivity(this) }
}
Kotlin 的函数参数不能命名为this ,因此我们在这里使用的self 和我们创建的apply 仍然必须通过self.name 访问成员变量,但是Kotlin 语言设计器可以为您做到这一点。
//改成这个
//
fun User.apply(block: (this: User) – 单位): User{
//这里需要传递参数
//
块(这个)
返回这个
}
用户?apply { this: 用户-
……
//选修的
//
用户名.文本=this.name
网站.文本=博客
image.setOnClickListener { gotoImagePreviewActivity(this) }
}
从上面的例子可以看出,我们推导的apply实现比较麻烦,需要我们自己调用block(this)。这就是为什么Kotlin 引入了带有接收器的函数类型,以简化apply 的定义。
//带接收者的函数类型
//
fun User.apply(block: User.() – Unit): User{
//不需要再次传递这个
//
堵塞()
返回这个
}
用户?apply { this: 用户-
……
用户名.文本=this.name
网站.文本=this.博客
image.setOnClickListener { gotoImagePreviewActivity(this) }
}
现在,关键就在这里。上面的apply方法看起来是不是给User添加了一个成员方法apply()?
类用户(){
值名称: 字符串=\”\”
val blog: 字符串=\”\”
有趣的应用(){
//成员方法可以通过this访问成员变量
用户名.文本=this.name
网站.文本=this.博客
image.setOnClickListener { gotoImagePreviewActivity(this) }
}
}
因此,具有接收者的函数类型在外部等同于成员方法。然而,本质上这是通过编译器注入来完成的。
图片摘要:
思考题2:
带有接收者的函数类型也可以代表扩展函数吗?
思考题3:
请问一个问题:A、(B,C)-D 代表什么函数?
4. HTML Kotlin DSL 实战
官方文档的高阶函数章节描述了使用高阶函数来实现类型安全的HTML 构建器。官方文档中的例子比较复杂,所以我们写一个简化版本来练习。
4-1 效果展示:
val html 内容=html {
头{
title { “Kotlin Jetpack 的工作原理” }
}
身体{
h1 {“Kotlin Jetpack 的工作原理”}
p { “———————————————————— ——– ——\” }
p { \”一个非常简单的项目,向您展示如何逐步使用Kotlin 和Jetpack\” }
p { “———————————————————— ——– ——\” }
p {“我让这个项目尽可能简单。”
\’让您专注于如何使用Kotlin 和Jetpack\’ +
而不是理解业务逻辑。
p { \”从\”Java + MVC\”重写为\”\” +
\’“Kotlin + 协程+ Jetpack + Clean MVVM”,\’ +
\’ 逐行提交,逐次提交。 \’}
p { “———————————————————— ——– ——\” }
p {“屏幕截图:”}
img(src=“https://user-gold-cdn.xitu.io/2020/6/15/172b55ce7bf25419?imageslim”,
alt=“Kotlin Jetpack 的工作原理”)
}
}.toString()
println(html内容)
上述代码的输出将如下所示:
Kotlin Jetpack 的工作原理
Kotlin Jetpack In Action
————————————–
一个非常简单的项目,向您展示如何逐步使用Kotlin 和Jetpack。
————————————–
我让这个项目尽可能简单,这样我就可以专注于如何使用Kotlin 和Jetpack,而不是理解业务逻辑。
将其从“Java + MVC”重写为“Kotlin + Coroutines + Jetpack + Clean MVVM”,逐行,逐次提交。
————————————–
截图:
4-2 HTML Kotlin DSL 实现
4-2-1 定义节点元素的接口
界面元素{
//每个节点必须实现render方法
有趣的渲染(builder: StringBuilder,indent: String): String
}
所有HTML 节点必须实现Element 接口,并在渲染方法中实现HTML 代码的拼接: title Kotlin Jetpack In Action /title
4-2-2 定义基础类
/**
每个节点都有一个名称。 content: Kotlin Jetpack 的工作原理
*/
打开类BaseElement(val name: String, val content: String=\”\”) : Element {
//每个节点有很多子节点
val 子级=ArrayList()
//存储节点参数:src、alt里面
val hashMap=HashMapString, String()
/**
HTML: Kotlin Jetpack 拼接行为
*/
覆盖乐趣渲染(builder: StringBuilder,indent: String): String {
生成器.追加(“
我
n
d
e
n
t
缩进
缩进名称\\n”)
if (content.isNotBlank()) {
生成器.append(\’
我
n
d
e
n
t
缩进
缩进内容\\n\’)
}
儿童.forEach {
it.render(builder, \”KaTeX 解析错误: 需要\’EOF\’ 但在位置12: 缩进\’) } builder.append.indent/$name\\n\”)
返回builder.toString()
}
}
4-2-3 定义各个子节点:
//这是HTML : 中最外面的标签
HTML 类: BaseElement(\”html\”) {
有趣的头(Block: Head.() – Unit): Head {
val 头=头()
head.block()
之后
ldren += head
return head
}
fun body(block: Body.() -> Unit): Body {
val body = Body()
body.block()
this.children += body
return body
}
}
// 接着是 标签
class Head : BaseElement(“head”) {
fun title(block: () -> String): Title {
val content = block()
val title = Title(content)
this.children += title
return title
}
}
// 这是 Head 里面的 title 标签 <br/>
class Title(content: String) : BaseElement(“title”, content)
// 然后是 标签
class Body : BaseElement(“body”) {
fun h1(block: () -> String): H1 {
val content = block()
val h1 = H1(content)
this.children += h1
return h1
}
fun p(block: () -> String): P {
val content = block()
val p = P(content)
this.children += p
return p
}
fun img(src: String, alt: String): IMG {
val img = IMG().apply {
this.src = src
this.alt = alt
}
this.children += img
return img
}
}
// 剩下的都是 body 里面的标签
class P(content: String) : BaseElement(“p”, content)
class H1(content: String) : BaseElement(“h1”, content)
class IMG : BaseElement(“img”) {
var src: String
get() = hashMap[“src”]!!
set(value) {
hashMap[“src”] = value
}
var alt: String
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取
img
}
}
// 剩下的都是 body 里面的标签
class P(content: String) : BaseElement(“p”, content)
class H1(content: String) : BaseElement(“h1”, content)
class IMG : BaseElement(“img”) {
var src: String
get() = hashMap[“src”]!!
set(value) {
hashMap[“src”] = value
}
var alt: String
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
[外链图片转存中…(img-yvcoM7Q3-1719091195081)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取
#以上关于Kotlin的相关内容来源网络仅供参考,相关信息请以官方公告为准!
原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/91808.html