前段时间在我的知识星球里统计了一下使用kotlin开发的人,感觉还不错,有十多个人在工作中已经正式使用了,我的知识星球《Hi Android》欢迎你的加入,我也应星友的需求写了一部分的kotlin基础知识,我也还在学习的过程中,为了帮助更多的星友认识到Kotlin for Android,所以花了点时间整理了这篇《Kotlin超车指南》,如果对你有所帮助,记得点个赞哦。
推荐我的慕课网Android实战课程,助你暴力提升Android技术。
Android X/音视频开发/社交匹配算法/即时通信/语音识别/App优化/安全加固
Kotlin 是一个用于现代多平台应用的静态编程语言,由JetBrains开发。
Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。
Kotlin已正式成为Android官方支持开发语言。
一.Kotlin下载
Kotlin支持命令行,Eclipse,Intellij IDEA,Android Studio 3.0+ 四种方式编程,那么我们学习Kotlin基础,肯定是优先前三者, 只有在开始Android开发的时候才会选择Android Studio
1.命令行
命令行安装的话需要去Github上下载工具包
选择 kotlin-compiler-xxx.zip 即可。
2.Eclipse安装
安装Eclipse之前需要安装JDK,这个没意见吧,然后去Eclipse官网下载:
当然,就像ADT一样,如果想要Eclipse支持Kotlin开发,还需要下载Kotlin的插件
不过离线安装一般都是老版本的Eclipse了,在新版本中可以使用Eclipse商店进行在线安装,点击Eclispe菜单 - help - Eclipse Markerplace 搜索kotlin 就可以了。
3.Intellij IDEA
IDEA安装起来还是很方便的,点击链接:
下载自由版本即可。
4.Android Studio
Android Studio 也是如此,安装即可。
二.Kotlin历史
1.Kotlin名称由来
Kotlin 来源于一个岛屿的名字,全称是 Kotlin Island,是英语「科特林岛」之意。这个小岛属于俄罗斯。
2.Kotlin版本迭代
2010 年 :JetBrains 着手开发 Kotlin。
2011 年 7 月 :JetBrains 公开宣布 Kotlin。
2012 年 2 月 :JetBrains 在 Apache 2 许可证下开源了 Kotlin 的源码
2016 年 2 月 :JetBrains 发布了 Kotlin 1.0 版,算是比较稳定的正式版。
2017 年 :1.1.2(2017 年 6 月)。相比 Kotlin 1.0 版本时,和 Kotlin 有关的代码数目从 210 万行飞升到 1000 多万行。
此后还在不断的更新中…
3.Kotlin语言特点
简洁
简洁时Kotlin最主要的特点。Kotlin中数据类,类型推导,Lambda表达式和函数式编程都可以大大减少代码行数,使得代码更加简洁。
安全
kotlin和Java一样都是静态类型语言
Kotlin支持非空和可空类型,默认情况下Kotlin数据类型声明的变量都是不能接受空值(null)的。
类型推导
Kotlin编译器可以根据变量所在的上下文环境推导出它的数据类型
支持函数式编程
作为现代计算机语言Kotlin支持函数式编程,函数式编程优点:代码变得简洁,增加线程安全和便于测试。
支持面向对象
Kotlin支持函数式编程,但也不排除面向对象。
Java具有良好的互操作性
Kotlin和Java具有100%互操作性,Kotlin不需要任何转换成包装就可以调用Java对象。反之亦然,Kotlin完全可以使用现有的Java框架或库
免费开源
Kotlin源代码时开源免费的,它采用Apache2 许可证。
三.Kotlin基础
1.输出
fun main(arg:Array<String>){
println("Hello Kotlin!")
}
main函数是我们的主程序入口,在控制台输出Hello Kotlin使用了println方法。
2.输入
有输出就有输入,不过输入是从外部传递的
fun main(arg:Array<String>){
println("请输入...")
var a = readLine()
println("你输入的内容是 $a")
}
3.变量与常量
fun main(arg:Array<String>){
var name = "张三"
name = "李四"
println(name)
val age = 18
println(age)
}
我们用var代表变量,即可变的量,用val声明常量,即不可变的量
4.数据类型
数据类型和Java是类似的
Byte Short String Int Long Float Double
5.类型推导
类型推导也是kotlin的一个特色,我们来看代码
fun main(arg:Array<String>){
var name:String
var age = 19
}
如果没有值就无法类型推导,则需要声明数据类型,如果有值,则可智能推导类型
6.函数
我们先来看下一段示例代码
//输出
fun print(text:String){
println(text)
}
//加法
fun add(a:Int,b:Int):Int{
return a+b
}
首先是我们的输出函数,实际上所有的函数都有返回值的,但是有些不需要返回值,所有返回了一个Unit类型,也就是我们理解的无返回值,而加法两数相加之后需要把结果返回回去,则有了Int的返回值类型。
a.参数默认值
有时候碰到一些封装的场景,有对函数值统一要求的时候,可以这样处理
fun main(arg:Array<String>){
println(add(1,2))
println(add(b = 2))
}
//参数默认值
fun add(a:Int = 5,b:Int):Int{
return a+b
}
我给a默认赋值了一个5,如果使用默认值,则需要指定b = 2,如果不用此默认值,则直接传值即可,比重载好用。
7.字符串模板
在Java中要拼接字符串的话,需要很多的+号,但是有了字符串模板,一切都会变得简洁起来了
fun main(arg:Array<String>){
val name = "张三"
val address = "超市"
val apple = 10
val banana = 5
//张三 去 超市 买了苹果和香蕉一共花了 15 元
print("$name 去 $address 买了苹果和香蕉一共花了 ${apple + banana} 元")
}
如果用java写,你可以想象要怎么拼接
8.条件判断
条件判断我们有if语句
fun main(arg:Array<String>){
val i = 10
if(i < 5){
println("i比5小")
}else{
println("i比5大")
}
}
9.循环
循环使用in即可,在后面区间中使用
10.空类型
在Java中可能会出现空指针异常,但是在kotlin中是可以避免这种问题的,我们来看下空类型判断
fun main(arg:Array<String>){
//noNull(null) 报错
println(yesNull(null))
}
//不能为null
fun noNull(text:String):String{
return "Hello $text"
}
//可以为null
fun yesNull(i:Int?):String{
if(i === null){
return "i == null"
}
return "Hello $i"
}
如果我传值指定了是String,那么null作为空类型是无法传入的,如果想要传入null,则需要加上问号?
11.选择表达式
在java中可以使用switch,在kotlin中则是when了
fun printScore(sex:Boolean,i:Int){
when(i){
100 -> print("最高成绩")
80 -> {
if(sex){
print("男生这个分数不行")
}else{
print("女这个分数还行")
}
}
else -> print("其他人都重考")
}
}
并且不管我们的if还是when都是有返回值的,默认为表达式的最后一行
fun printScore(sex:Boolean,i:Int){
val result = when(i){
100 -> 100
80 -> {
if(sex){
80
}else{
90
}
}
else -> 60
}
print("result $result");
}
12.区间
区间很好理解,就是某个数到某个数之间,这里来计算一下从0到100的数字之和
fun main(arg:Array<String>){
var a = 0..100
var result = 0
for(num in a){
result = result + num
}
println("从0到100的数字总和: $result")
}
区间还有开区间(a…b)和闭区间[a…b]的概念
闭区间包含了端点的两个值
开区间不包含端点的两个值
13.List
fun main(arg:Array<String>){
var a = listOf("吃早餐",1,"睡午觉",2)
for(list in a){
//取值
println(list)
}
for((k,v) in a.withIndex()){
//取下标与值
println("k $k , v $v")
}
}
我们可以通过listOf来创建list
14.map
键值对的存储,我们先简单理解一下
fun main(arg:Array<String>){
var map = mapOf("Java" to 86, "Kotlin" to 92, "Go" to 78)
for((k,v) in map){
println("k $k , v $v")
}
}
我们通过mapOf返回一个不可变的map
15.函数式编程
函数式编程是不同于过程式编程的另一种编程范式。函数式编程的思想在许多方面和过程式是冲突的,比如,过程式编程倾向于描述“怎么做”,而函数式编程则更倾向于描述“做什么”,过程式倾向于使用变量,而函数式则倾向于使用常量。尽管如此,函数式和过程式依旧是可以共存的,我们来看一个简单的例子:
fun add(a:Int,b:Int):Int{
return a+b
}
fun add1(a:Int,b:Int) = a+b
val add2 = {a:Int,b:Int -> a+b}
上一个是标准的函数写法, 而中间,则将表达式作为返回值,而add2则直接声明一个val接收参数计算,这完全就打破了我们之前的编程写法。
16.数据类型转换
类型转换用的最多的就是字符串转Int了,我们看代码:
fun main(arg:Array<String>){
var a = "12"
println(a.toInt())
}
其他转换也类型toLong,toString等。
17.递归
递归有两个概念,递归和尾递归,相当于调用自身,我们在java中也学过,再来看下尾递归,尾递归是一个新的概念,他的重要性在于它可以不在调用栈上面添加一个新的堆栈帧,而是更新它,如同迭代一般。
比如我们累加一个超级大的数,如果使用递归会出现堆栈溢出的吗,但是尾递归不会
fun main(arg:Array<String>){
var result = 0
println(add(100,result))
}
tailrec fun add(i:Int,result:Int):Int{
//println("result:$result")
if(i == 0){
return result
}
return add(i - 1,result + i)
}
18.单例模式
单例模式在java中有很多写法,但是在kotlin中就简单多了,只需要将class改为object即可,相当于在jvm内存中直接开辟了一块唯一内存地址。
但是使用object只是相当于Java的饿汉式,如:
//Java
public class JavaSingleton {
private static JavaSingleton instance= new JavaSingleton();
private JavaSingleton(){
}
public static JavaSingleton getInstance(){
return instance;
}
}
//Kotlin
object KotlinSingleton
如果我们需要实现双重校验的单例模式的话,需要利用伴生对象和lazy来实现:
//Java
public class JavaSingleton {
private volatile static JavaSingleton instance;
private JavaSingleton(){}
public static JavaSingleton getInstance(){
if(instance==null){
synchronized (JavaSingleton.class){
if(instance==null){
instance=new JavaSingleton();
}
}
}
return instance;
}
}
//kotlin
class KotlinSingleton private constructor() {
companion object {
val instance: KotlinSingleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { KotlinSingleton() }
}
}
19.枚举类
枚举很简单,稍微看看就行
fun main(args: Array<String>) {
println("现在是 ${Season.WINTER.name} 位于枚举第 ${Season.WINTER.ordinal} 个元素 ")
}
//季节
enum class Season {
SPRING,
SUMMER,
AUTUMN,
WINTER
}
20.印章类
印章类又称为密封类,用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而它的一个子类可以有可包含状态的多个实例
fun main(args: Array<String>) {
var a = Son.chinaSon()
var b = Son.foreignSon()
a.Hello()
b.Hello()
}
sealed class Son{
fun Hello(){
println("我是孩子")
}
class chinaSon:Son()
class foreignSon:Son()
}
可以看到,假设中国人和外国人在一起生出的孩子,有可能是中国人的样貌,有可能是外国人的样貌取决于基因,那么我通过sealed来修饰。
21.数据类
数据类使用data修饰即可
22.延迟加载
延迟加载在kotlin中有两种,lateinit var 和 by lazy ,在kotlin中,默认属性都是空安全的,所以声明必须有初始化值,但是我们有时候是没有初始值的,这个时候就需要延迟加载了。
lateinit var 只能用于类中
class People {
//报错
var names:String
lateinit var name:String
}
而by lazy 作为惰性加载,其使用第一次的时候才会为你加载
fun main(args: Array<String>) {
val p by lazy { People() }
}
我们一般常用的还是lazy,不过需要注意的是
1.by lazy 只能作为val关键字的属性
2.当属性用到的时候才会回调括号内的内容
23.伴生对象
伴生对象的关键字是companion,为外部模拟静态成员,可以半理解为static
class People {
companion object PeopleInstnce {
const val name: String = "张三"
}
}
fun main(args: Array<String>) {
println("name ${People.name}")
println("name ${People.PeopleInstnce.name}")
}
要注意的是,每个类只能定义一个伴生对象,这个伴生对象相当于外部类的对象,可以直接通过外部类名访问伴生对象的成员,这也是取消了static的一个弥补手段。
24.内联函数
内联的作用主要还是为性能这块所考虑,我们每一次调用高阶函数的时候,都会创建一个新的对象,所以你可以发现高阶函数都是内联函数,内联函数的关键字是lnline
当一个函数被声明为inline时,它的函数体是内联的,也就是说,函数体会被直接替换到函数被调用地方。
四. 面向对象
1.对象
和java一样,我们来看下
class People(var name: String, var sex: Boolean)
fun main(args: Array<String>) {
var p = People("张三", true);
println("人类${p.name}他的性别是${if (p.sex) "男" else "女"}")
}
可以看到这里class定义一个类,然后声明他的参数,不需要去创建他的构造函数,实际上,这就是他的构造函数
那么构造函数,这里又有一些区分了,在kotlin中,有主构造和次构造的区别
而我们上述的写法真实情况是这样的,只不过我们一般省略关键字
class People constructor(var name: String, var sex: Boolean)
我们如果没有在主构造中对变量进行var/val的声明,则可以在init语句中进行赋值
class People(name: String, sex: Boolean) {
private var name: String? = ""
private var sex: Boolean? = false
init {
this.name = name
this.sex = sex
}
}
当然,你也可以直接赋值,这也算写法上的演进
class People(name: String, sex: Boolean) {
private var name: String? = name
private var sex: Boolean? = sex
}
那么再来看下次构造,在Java中,构造函数是必须和类同名的,但是kotlin中则不是,我们可以这样
class People {
constructor(name: String)
constructor(name: String, sex: Boolean)
}
fun main(args: Array<String>) {
People("张三")
People("李四", true)
}
2封装
封装和java是一样的,只要不对外提供的代码进行private修饰即可,默认public
3.继承
继承的概念和java类似,但是还是有一些不一样的,我们来看下代码
open class People(var name: String, var sex: Boolean) {
open fun Eat() {
println("People Eat")
}
open fun Sleep() {
println("People Sleep")
}
open fun Character() {
println("People Character")
}
}
class Boy(name: String, sex: Boolean) : People(name, sex) {
override fun Character() {
println("Boy Character")
}
}
fun main(args: Array<String>) {
val boy = Boy("张三",true);
boy.Eat()
boy.Sleep()
boy.Character()
}
在这段代码中,我定义了一个People,他有吃,睡,性格,三个函数,现在一个男孩继承了他,并且重写了性格的函数,因为每个人的性格都是不一样的,这里和java需要注意的问题就是,在java中我们继承了就可以直接使用父类,但是在kotlin中,父类需要标记open这个关键字才能让人继承,并且子类重写方法需要加上overide
4.抽象
我们看继承贴出的那些代码片段可以发现,其实很多东西我们可以优化的,比如People作为父类没必要实现具体操作,可以抽象出来:
abstract class People(var name: String, var sex: Boolean) {
abstract fun eat()
abstract fun sleep()
abstract fun character()
}
class Boy(name: String, sex: Boolean) : People(name, sex) {
override fun eat() {
println("Boy eat")
}
override fun sleep() {
println("Boy sleep")
}
override fun character() {
println("Boy character")
}
}
fun main(args: Array<String>) {
val boy = Boy("张三", true);
boy.eat()
boy.sleep()
boy.character()
}
可以看到,我们通过abstract去优化了这个抽象类,而Boy类具体去实现
5.多态
多态我们可以看抽象类的延伸,即同种功能不同的实现
abstract class People(var name: String, var sex: Boolean) {
abstract fun character()
}
class Boy(name: String, sex: Boolean) : People(name, sex) {
override fun character() {
println("$name ${if (sex) "调皮" else "乖巧"}")
}
}
class Girl(name: String, sex: Boolean) : People(name, sex) {
override fun character() {
println("$name ${if (sex) "调皮" else "乖巧"}")
}
}
fun main(args: Array<String>) {
val boy = Boy("张三", true);
boy.character()
val girl = Girl("李四", false);
girl.character()
}
比如一个People的性格,男人和女人实现的内容是不一样的。
6.接口
接口使用的关键字是interface,比如我模拟一下点击事件
fun main(args: Array<String>) {
//点击事件
click(object :OnClickListener{
override fun onClick(position: Int) {
println("position:$position")
}
})
}
fun click(listener: OnClickListener){
listener.onClick(2)
}
interface OnClickListener {
fun onClick(position: Int)
}
7.委托和代理
委托和代理是相辅相成的,打个比方,政府招商的一块地,一千万委托给了开发商A,这个时候就是政府委托开发商,而开发商代理了政府,那么这个时候开发商A已一百万的价格委托给了开发商B,从中牟利了九百万,也就导致了现在的豆腐渣工程了,那么我们用代码来实现吧。
fun main(args: Array<String>) {
val build = developersA("一千万")
build.buildHome()
}
class developersB(var price: String) : IBuildHome {
override fun buildHome() {
println("建造房子花费$price")
}
}
class developersA(var price: String) : IBuildHome by developersB("一百万")
//建造方式的能力
interface IBuildHome {
fun buildHome()
}
可以看到我们是通过by关键字进行委托的,developersA 获得一千万后给developersB一百万依旧把房子建造出来了。
五.函数式编程
我们在讲kotlin基础的时候实际上是有讲函数式编程的,但是那些都比较粗糙了,这一章节我们讲细一点
1.闭包
我们需要先理解闭包的概念,因为后面讲高阶函数的时候
1.闭包指的是函数的运行环境
2.闭包可以持有函数的运行环境
3.函数内部可以定义函数
4.函数内部也可以定义类
5.在函数中返回一个函数,被返回的函数可以调用主函数的属性
这样可能理解有点抽象,事实上,作为kotlin的特性之一,闭包的用途还是很广泛的,全局变量,顾名思义,其作用域是当前文件甚至文件外的所有地方;而局部变量,我们只能再其有限的作用域里获取。
那么,如何在外部调用局部变量呢?答案就是——闭包,与此给闭包下个定义:闭包就是能够读取其他函数内部变量的函数
fun main(args: Array<String>) {
val add = add()
add()
add()
add()
//输出 1 2 3
}
fun add(): () -> Unit {
var count = 0
return fun() {
println(++count)
}
}
这段代码中可以看到输出的是 1 2 3 ,也就是在return的fun中引用了外部的count
2.forEach
forEach是高阶函数之一,我们先来看下他的用法
fun main(args: Array<String>) {
val lists = listOf(1,2,3,4,5,6)
lists.forEach {
println(it)
}
}
直接就能打印lists的参数,而要想了解他的工作原理,我们最好来看下他的源码
/**
* Performs the given [action] on each element.
*/
@kotlin.internal.HidesMembers
public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
for (element in this) action(element)
}
可以看到他是Iterable的扩展函数,接收的是一个action参数,T就是list的类型,也就是
带有Int类型无返回值的函数
而内部则是通过for遍历传入下标得到具体值
还有一个forEachIndexed则是求下标和具体值的,这个自己理解一下
3.apply
apply函数扩展了所有的泛型对象,在闭包范围内可以任意调用该对象的任意方法,并在最后返回该对象.
我们来看一段示例代码
class People {
val name: String = "小明"
val age: Int = 18
}
fun main(args: Array<String>) {
//方式一
val p1 = People();
println("p1 ${p1.name} ${p1.age} ")
//方式二
People().apply {
println("p1 $name $age")
}
}
在闭包内我们可以随意获取和变化参数并且返回对象本身,我们来看下他的源码
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
这段代码中T表示对象本身,并且内部又一个契约contract,用于上下文推断。
4.maxBy
求最大值
fun main(args: Array<String>) {
val lists = listOf(11,2,30,49,50,6)
val max = lists.maxBy { it }
println(max)
}
我们可以传递一个类型进去,他则返回一个类型的最大值,来看下源码
public inline fun <T, R : Comparable<R>> Iterable<T>.maxBy(selector: (T) -> R): T? {
val iterator = iterator()
if (!iterator.hasNext()) return null
var maxElem = iterator.next()
if (!iterator.hasNext()) return maxElem
var maxValue = selector(maxElem)
do {
val e = iterator.next()
val v = selector(e)
if (maxValue < v) {
maxElem = e
maxValue = v
}
} while (iterator.hasNext())
return maxElem
}
这段源码实际上就是在遍历获得最大值,通过R返回回去,当然,还有minBy求最小值
5.fifter
过滤函数,要啥RxJava,要啥自行车,kotlin美滋滋
fun main(args: Array<String>) {
val lists = listOf(11, 2, 30, 49, 50, 6)
//求大于20且小于50的值
val filter = lists.filter { (it > 20) and (it < 50)}
println(filter)
}
这里可以多条件判断,只要符合规范即可。
看源码得知最终调用的是filterTo
6.map
map作为数据转换,根据规则创建新的集合
fun main(args: Array<String>) {
val lists = listOf(11, 2, 30, 49, 50, 6)
val filter = lists.map {
it.toFloat()
}
println(filter)
}
这里我对值进行了toFloat的操作,则全部类型变换为float.
看源码得知最终调用的是mapTo,但是可以知道是创建了一个ArrayList进行的返回
7.any
any更多的是作为一个判断函数使用
fun main(args: Array<String>) {
val lists = listOf(11, 2, 30, 49, 50, 6)
//集合中是否有30这个值
val filter = lists.any {
it == 30
}
println(filter)
}
来看下源码
public inline fun <T> Iterable<T>.any(predicate: (T) -> Boolean): Boolean {
if (this is Collection && isEmpty()) return false
for (element in this) if (predicate(element)) return true
return false
}
传递的参数是一个T的集合类型返回的是一个布尔的函数,可以看到他的实现也是通过for循环遍历的
8.count
count作为满足条件的统计函数,我们可以这样使用
fun main(args: Array<String>) {
val lists = listOf(56, 77, 30, 49, 100, 63)
//统计及格的同学人数
val filter = lists.count {
it >= 60
}
println(filter)
}
从源码中也可以看出是通过for循环去遍历统计size的
9.find
查找函数,只返回满足条件的第一个
fun main(args: Array<String>) {
val lists = listOf(56, 77, 30, 49, 100, 63)
//查找第一个及格的同学
val filter = lists.find {
it >= 60
}
println(filter)
}
看源码得知最终调用的是firstOrNull
10.groupBy
这是分组函数,用于一个特定条件的分组
fun main(args: Array<String>) {
val lists = listOf(56, 77, 30, 49, 100, 63)
//及格和不及格的分组
val groupBy = lists.groupBy {
it >= 60
}
println(groupBy)
}
比如这个示例,我的条件是大于等60则一组,那么其余为一组,可得及格和不及格的分组信息
11.let
let的作用主要用于避免非空的判断和作用域替换
name?.let{
//只有不为空才会走进来
}
六.DSL
DSL领域特定语言,即好玩又好吃的语法糖。
1.扩展函数
Kotlin的扩展函数可以让你作为一个类成员进行调用的函数,但是是定义在这个类的外部。这样可以很方便的扩展一个已经存在的类,为它添加额外的方法。在Kotlin源码中,有大量的扩展函数来扩展java,这样使得Kotlin比java更方便使用,效率更高。
我们来举个例子:
fun main(args: Array<String>) {
val lists = listOf(56, 77, 30, 49, 100, 63)
println(lists.getIndex())
}
//扩展:获取第一个下标
fun List<Int>.getIndex():Int{
return get(0)
}
我现在想获取List的第一个值,但是如果List并没有这个方法,我则可以直接给予他扩展。
2.中缀表达式
像我们的and or in 都是中缀表达式,实际上就是省略了点的操作
fun main(args: Array<String>) {
Boy().love(Girl())
Girl() love Boy()
}
class Boy{
fun love(girl: Girl){
//普通写法
}
}
class Girl{
infix fun love(boy: Boy){
//中缀表达式
}
}
可以看到,是相当的有趣。
七.Kotlin For Android
关于Kotlin开发Android应用的时候的一些区别
1.创建项目
创建项目的时候,在Configure your peoject 这一项的时候选择kotlin作为开发语言即可
大体的东西都不会变化,只是增加了对Kotlin的支持,包括在project/build.gradle中增加了kotlin的插件
以及在app/build.gradle中增加了kotlin的stdlib和core库,当然还包括了kotlin的扩展库
2.findViewById
kt是不需要自己手动findviewbyid的,我们可以用个扩展插件来完成,先来看一段代码
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mButton.text = "Button"
mButton.setOnClickListener {
Log.i("TAG", "Button Click")
}
}
}
在这段代码中,我们引入了一段包名
import kotlinx.android.synthetic.main.activity_main.*
我们就可以直接省略初始化的操作,使用id就可以了
这个功能就是kotlin扩展库的功能了
3.跳转界面
//Java
startActivity(new Intent(this,FirstActivity.class))
//Kotlin
startActivity(Intent(this,FirstActivity::class.java))
跳转主要还是在class类上,我们可以看到Kotlin需要::class.java
4.匿名内部类
mButton.setOnClickListener(object :View.OnClickListener{
override fun onClick(v: View?) {
//Anything
}
})
//可简化
mButton.setOnClickListener {
}
5.Anko
这是一个优秀的kotlin辅助库,也可以称之为工具库:
a.代码布局
可以使用它的一些语法糖来实现代码写布局,虽然我觉得这个使用率肯定是不高的
verticalLayout {
gravity = Gravity.CENTER
val name = editText()
val password = editText()
name.hint = "请输入账号"
password.hint = "请输入密码"
button("登录") {
onClick {
toast("登录成功")
}
}
}
我们来运行看一下
b.Intent 优化
intent跳转也相对的简化了
//需要设置FLAG
startActivity(intentFor<FirstActivity>("id" to 5).singleTop())
//需要传递参数
startActivity<FirstActivity>("id" to 5)
//仅适用跳转
startActivity<FirstActivity>()
c.其他
还有log的优化,以及toast,dialog等,大家自己去发现吧。
八.实战案例:天气预报
这里我准备写一个Kotlin版本的天气预报来给各位演示下一些Koltin语法的基本使用,先来看下效果图
功能也很简单,共四个页面
- 1.启动页,缩放动画
- 2.城市选择页,自定义View实现城市选择,RecyclerView与城市选择View双向绑定和联动
- 3.主页,天气详情,RecyclerView显示五天天气
- 4.设置页,重新选择城市等
使用的接口是聚合数据
使用的网络请求库是retrofit2,好了,我们开始吧;
1.启动页
启动页就一个动画
这里使用的是ViewCompat自带的动画,追其根源的话最终还是属性动画实现,监听动画结束之后就可以判断是否选择过城市了,选择过就直接进入主页,没有的话就进入城市选择页,这里作为持久化保存,我是通过Kotlin的单例封装的一个SharedPreferences类
2.城市选择页
城市选择页比较麻烦,我们先来定义请求网络的接口
然后就是一层简单的封装了
这里首先是对类进行了object的单例化,然后对retrofit2进行了延迟初始化,当我们加载城市列表成功之后,我们就将数据设置给我们的侧边城市选择View,这里我贴一下核心的代码
首先就是绘制了,将34个城市名称按照View的高度进行平均分配和绘制,然后紧接着就是滑动的处理了
这样,我们就可以很轻松的实现列表的滑动了,但是这里又有一个问题了,那就是联动的问题
a.View -> RecyclerView
先看选择View控制列表的滑动
我这里的做法就是根据他的滑动坐标点,找到对应的城市名,然后根据城市名去过滤城市列表以此来找到index,这样就可以滚动列表了
b.RecyclerView -> View
这个是反向的操作,我们需要监听列表的滚动
得到第一个可见的下标后以此来推敲出城市名,然后去根据城市名得到index,让View去刷新即可
3.主页
主页的操作其实就是按部就班的将请求的数据显示出来
4.设置页
设置页也是一样的,这里多了个粘贴板的操作罢了
5.Gif预览
最后我们来预览下Gif
九.结语
Kotlin之美还有很多,一文很难讲完,但是循序渐进,还是可以看到效果的,这篇文章的初衷还是希望带领大家走进这门语言,虽然Google强调Kotlin First ,但是就目前而言还只是应用App还没设计到Android 源码层的改动,所以还是百花齐放的阶段,留给大家的时间还是有的,希望大家再接再厉吧。
Github地址:
推荐我的慕课网Android实战课程,助你暴力提升Android技术。
Android X/音视频开发/社交匹配算法/即时通信/语音识别/App优化/安全加固
写了这么多,点个小爱心不过分吧,嘿嘿。