添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
俊秀的拐杖  ·  Android - ...·  12 小时前    · 
痛苦的充电器  ·  Kotlin compiler ...·  2 天前    · 
逆袭的可乐  ·  SonarLint IntelliJ ...·  2 天前    · 
坏坏的小熊猫  ·  Use platform-specific ...·  2 天前    · 
果断的小虾米  ·  variable expected ...·  2 天前    · 
开朗的牛排  ·  解读黎曼猜想 - 脉脉·  3 月前    · 

1、无需 findViewById

在布局中定义

1
2
3
4
5
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />

直接设置 TextView 的文本

1
tv_content.text = "改变文本"

2、Lambda

1
2
3
4
5
tv_content.setOnClickListener(View.OnClickListener(
fun(v : View) {
v.visibility = View.GONE
}
))

可以写成下面

1
tv_content.setOnClickListener { v -> v.visibility = View.GONE }

3、函数变量

在 kotlin 语法中函数是可以作为变量进行传递的

1
2
3
var result = fun(number1 : Int, number2 : Int) : Int {
return number1 + number2
}

使用这个函数变量

1
println(result(1, 2))

4、空安全

在 Java 不用强制我们处理空对象,所以常常会导致 NullPointerException 空指针出现,现在 Kotlin 对空对象进行了限定,必须在编译时处理对象是否为空的情况,不然会编译不通过.

在对象不可空的情况下,可以直接使用这个对象.

1
2
3
4
5
6
fun getText() : String {
return "text"
}

val text = getText()
print(text.length)

在对象可空的情况下,必须要判断对象是否为空

1
2
3
4
5
6
7
fun getText() : String? {
return null
}
val text = getText()
if (text != null) {
print(text.length)
}

如果不想判断是否为空,可以直接这样,如果 text 对象为空,则会报空指针异常,一般情况下不推荐这样使用

1
2
val text = getText()
print(text!!.length)

还有一种更好的处理方式,如果 text 对象为空则不会报错,但是 text.length 的结果会等于 null

1
2
val text = getText()
print(text?.length)

5、方法支持添加默认函数

在 Java 上,我们可能会为了扩展某个方法而进行多次重载

1
2
3
4
5
6
7
8
9
10
11
public void toast(String text) {
toast(this, text, Toast.LENGTH_SHORT);
}

public void toast(Context context, String text) {
toast(context, text, Toast.LENGTH_SHORT);
}

public void toast(Context context, String text, int time) {
Toast.makeText(context, text, time).show();
}
1
2
3
toast("弹个吐司");
toast(this, "弹个吐司");
toast(this, "弹个吐司", Toast.LENGTH_LONG);

但是在 Kotlin 上面,我们无需进行重载,可以直接在方法上面直接定义参数的默认值

1
2
3
4
5
6
7
fun toast(context : Context = this, text : String, time : Int = Toast.LENGTH_SHORT) {
Toast.makeText(context, text, time).show()
}

toast(text = "弹个吐司")
toast(this, "弹个吐司")
toast(this, "弹个吐司", Toast.LENGTH_LONG)

6、类方法扩展

可以在不用继承的情况下对扩展原有类的方法,例如对 String 类进行扩展方法

1
2
3
4
5
6
fun String.handle() : String {
return this + "Android轮子哥"
}
// 需要注意,handle 方法在哪个类中被定义,这种扩展只能在那个类里面才能使用
print("HJQ = ".handle())
HJQ = Android轮子哥

7、运算重载符

在 Kotlin 中使用运算符最终也会调用对象对应的方法,我们可以通过重写这些方法使得这个对象支持运算符,这里不再演示代码

8、扩展函数

扩展函数是 kotlin 用于简化一些代码的书写产生的,其中有 let、with、run、apply、also 五个函数。

let 函数

在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式。

1
2
3
4
5
6
fun main() {
val text = "Android轮子哥"
println(text.length)
val result = 1000
println(result)
}

let 写法

1
2
3
4
5
6
7
fun main() {
val result = "Android轮子哥".let {
println(it.length)
1000
}
println(result)
}

最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理

1
2
3
4
5
6
7
8
mVideoPlayer?.setVideoView(activity.course_video_view)
mVideoPlayer?.setControllerView(activity.course_video_controller_view)
mVideoPlayer?.setCurtainView(activity.course_video_curtain_view)
mVideoPlayer?.let {
it.setVideoView(activity.course_video_view)
it.setControllerView(activity.course_video_controller_view)
it.setCurtainView(activity.course_video_curtain_view)
}

又或者是需要去明确一个变量所处特定的作用域范围内可以使用。

with 函数

前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式

定义 Person 类

1
class Person(var name : String, var age : Int)
1
2
3
4
5
6
fun main() {
var person = Person("Android轮子哥", 100)
println(person.name + person.age)
var result = 1000
println(result)
}

with 写法

1
2
3
4
5
6
7
fun main() {
var result = with(Person("Android轮子哥", 100)) {
println(name + age)
1000
}
println(result)
}

适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上

1
2
3
4
5
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
holder.nameView.text = "姓名:${item.name}"
holder.ageView.text = "年龄:${item.age}"
}
1
2
3
4
5
6
7
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
with(item){
holder.nameView.text = "姓名:$name"
holder.ageView.text = "年龄:$age"
}
}
run 函数

实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式

1
2
3
4
var person = Person("Android轮子哥", 100)
println(person.name + "+" + person.age)
var result = 1000
println(result)

run 写法

1
2
3
4
5
6
var person = Person("Android轮子哥", 100)
var result = person.run {
println("$name + $age")
1000
}
println(result)

适用于let,with函数任何场景。因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理,这里还是借助 onBindViewHolder 案例进行简化。

1
2
3
4
5
6
7
8
9
10
11
12
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
holder.nameView.text = "姓名:${item.name}"
holder.ageView.text = "年龄:${item.age}"
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
item?.run {
holder.nameView.text = "姓名:$name"
holder.ageView.text = "年龄:$age"
}
}
apply 函数

从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身

1
2
3
val person = Person("Android轮子哥", 100)
person.name = "HJQ"
person.age = 50

apply 写法

1
2
3
4
val person = Person("Android轮子哥", 100).apply {
name = "HJQ"
age = 50
}

整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。

或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到。

1
2
3
4
5
mRootView = View.inflate(activity, R.layout.example_view, null)
mRootView.tv_cancel.paint.isFakeBoldText = true
mRootView.tv_confirm.paint.isFakeBoldText = true
mRootView.seek_bar.max = 10
mRootView.seek_bar.progress = 0

使用 apply 函数后的代码是这样的

1
2
3
4
5
6
mRootView = View.inflate(activity, R.layout.example_view, null).apply {
tv_cancel.paint.isFakeBoldText = true
tv_confirm.paint.isFakeBoldText = true
seek_bar.max = 10
seek_bar.progress = 0
}

多层级判空问题

1
2
3
4
5
6
7
8
9
10
11
if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) {
return;
}
if (mSectionMetaData.questionnaire.userProject != null) {
renderAnalysis();
return;
}
if (mSectionMetaData.section != null && !mSectionMetaData.section.sectionArticles.isEmpty()) {
fetchQuestionData();
return;
}

kotlin的apply函数优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mSectionMetaData?.apply {

//mSectionMetaData不为空的时候操作mSectionMetaData

}?.questionnaire?.apply {

//questionnaire不为空的时候操作questionnaire

}?.section?.apply {

//section不为空的时候操作section

}?.sectionArticle?.apply {

//sectionArticle不为空的时候操作sectionArticle

}
also 函数

also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
val result = "Android轮子哥".let {
println(it.length)
1000
}
println(result) // 打印:1000
}


fun main() {
val result = "Android轮子哥".also {
println(it.length)
}
println(result) // 打印:Android轮子哥
}

适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用

总结

通过以上几种函数的介绍,可以很方便优化kotlin中代码编写,整体看起来几个函数的作用很相似,但是各自又存在着不同。使用的场景有相同的地方比如run函数就是let和with的结合体

9、协程

子任务协作运行,优雅的处理异步问题解决方案。

协程实际上就是极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能。

https://www.kotlincn.net/docs/reference/coroutines/coroutines-guide.html

缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia根目录)执行以下命令:
npm i hexo-generator-json-content --save

3、在根目录_config.yml里添加配置: jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true