Android开发kotlin我爱编程

Kotlin项目实践指南(上)

2017-10-23  本文已影响169人  江湖再见2024

Kotlin项目实践指南(上)

关于作者

郭孝星,程序员,吉他手,主要从事Android平台基础架构方面的工作,欢迎交流技术方面的问题,可以去我的Github提issue或者发邮件至guoxiaoxingse@163.com与我交流。

文章目录

注:文章中"举例"字样代表所举的例子,"区别"字样代码Kotlin与Java不同的地方,

更多文章请参见文章目录

本篇文章是本系列文章的开篇文章,主要介绍Java中的编程概念(语句、函数、类等)是如何映射到Kotlin的代码中的。本文的目的在于帮助大家快速理解Java里的语法在Kotlin中如何实现,以及
它们之间有什么区别。

Kotlin是一种在Java虚拟机上运行的静态类型编程语言。

写在前面的总结,从Java过度到Kotlin,有哪些被替换的地方。🤔

Android Stduio 3.0已经正式支持Kotlin,如果你是第一次接触Kotlin,你可以看一下下面3篇文章,跑一下Demo,体会一下Kotlin。

一 表达式与运算符

1.1 控制表达式

Kotlin常见的控制语句有:

Kotlin的控制语句和Java有所区别🤔,它们具备更强大的功能,在介绍控制语句之前,我们要先理解两条规则:

在Kotlin中,if是表达式,而在Java中if是语句。

关于Kotlin的表达式,有两条规则需要我们理解:

在Java中所有的控制语句都是表达式,而在Kotlin中,除了循环(for、do与do/while)是语句外,其他大部分控制结构都是表达式,都有值。
而恰恰在Java中是表达式的赋值操作(a = 1)在Kotlin中却是语句,这样有助于避免比较操作(==)和赋值(=)操作之间的混淆。

有了上面两条规则,我们不难理解以下例子。

举例

val a = 1
val b = 2

val ifResult = if (a > b) a else b

val whenResult = when (a > b) {
    true -> a
    else -> b
}

val number = try {
    Integer.parseInt("1")
} catch (e: NumberFormatException) {
    null
}

for (i: Int in 1..100){
    Logger.d(i)
}

for((key, value) in map){
````//使用key,value
}

//变量 a 和 b 的值取自对集合中的元素上调用 component1() 和 component2() 的返回值。
for ((a, b) in collection) { …… }

1.2 操作符

Kotlin提供了非常多类似于RxJava的操作符,为日常的开发提供了极大的方便。更多操作符可以参考操作符列表

1.3 中缀调用

函数还可以用中缀表示法调用,当它们是成员函数或扩展函数;它们只有一个参数;它们用 infix 关键字标注。

/**
 * 用infix定义一个中缀函数
 */
infix fun Int.shl(x: Int) {
    println("I am a infix method")
}

/**
 * 中缀调用
 */
fun useInfixMethod() {
    1 shl 2
}

1.4 解构声明

解构声明是将一个对象解构成多个变量,一个解构声明会同时创建多个变量。

要实现解构声明,我们需要添加componentN函数,componentN函数指定要返回的域,并使用operator标记

class Teacher(val name: String, val age: Int) : Person(name, age), IBehavior {

    override fun talk() {
        super<Person>.talk()
        super<IBehavior>.talk()
    }

    operator fun component1(): Any {
        return name
    }

    operator fun component2(): Any {
        return age
    }
}

然后我们就可以使用解构声明批量的创建变量,解构赋值是按照omponentN函数的顺序来执行的。

val teacher = Teacher("LiLei", 20)
val (name, age) = teacher

Logger.d("name: " + name)
Logger.d("age: " + age)

如果我们在解构声明中不需要某个变量,用下划线代替即可。

val (_, age) = teacher

二 函数和变量

函数的声明以fun关键字开始,函数名称紧跟其后,然后是形参列表,形参列表后面是返回值,如果没有指定返回值,默认的返回值是Unit。

举例

fun max(a: Int, b: Int): Int {
    return if (a > b) a else b
}

如果函数中只有一个表达式,可以将这个表达式作为完整的函数体,称为表达式函数体,例如:

fun max(a: Int, b: Int): Int = if (a > b) a else b

Kotlin变量的声明从关键字var与val开始,Kotlin会自动进行类型推导,因此你无需显式的声明类型。

举例

val name =  "Nick"
var age = 10

默认情况下,应该尽可能的使用val关键字来声明变量,仅在必要的时候使用var,使用不可变引用,不可以对象以及无副作用的函数可以让你的代码更加接近函数式编程风格。

注:这里提一下Kotlin的package结构,和Java一样Kotlin也有包的概念,但是Kotlin对源码的文件组织更为自由,多个类可以在同一个文件中,文件的名字和目录也可以随意选择,但是我们还是
会按照Java的目录布局去组织源码,这是一种良好的实践,但在Kotlin中很多类都非常小,例如数据类,这种情况下,我们通常把它放在一个文件里。

2.1 默认参数值

在Java中定义函数时,有些参数有默认值,这就导致会定义大量的重载函数(参考Thread的构造函数),而在Kotlin中可以利用参数的默认值来避免这个问题。

/**
 * 函数可以带默认参数,@JvmOverloads注解可以在编译时生成重载函数
 */
@JvmOverloads
fun maxWithDefault(a: Int = 1, b: Int = 2): Int {
    return if (a > b) a else b
}

注:但是Java中没有参数默认值的概念,为了方便Java调用Kotlin,可以用@JvmOverloads去注解它,@JvmOverloads注解可以在编译时生成重载函数。

2.2 顶层函数和属性

回想一下,我们的项目中是不是有很多Utils类,它们一种静态的方式被我们调用。这些Utils类本身没有实际意义,只是作为函数的容器,而在Kotlin中我们无需
这么做,我们可以把这些函数和属性直接定义在代码文件的顶层,不用从属任何类,然后用一种导包的方式使用它。

定义

/**
 * 顶层函数,可以直接导包然后使用
 */
val topProperty = "I am a top property"

/**
 * 顶层函数,可以直接导包然后使用
 */
fun topMethod(property: String) {
    println("I am a top method")
}

class Method {

}

调用

import com.guoxiaoxing.kotlin.demo._02_method.topMethod
import com.guoxiaoxing.kotlin.demo._02_method.topProperty

object MethodClient {

    @JvmStatic
    fun main(args: Array<String>) {
        topMethod(topProperty)
    }
}

2.3 扩展函数和属性

Kotlin的一大优势就在于它可以平滑的与Java进行集成,例如我们想要用Kotlin为Java里的类添加一个方法,这时我们不要去修改Java的源码,而是可以使用扩展函数与属性。


/**
 * 为String类添加一个扩展属性extensionProperty,这个String类可以是Kotlin、Java甚至其他任何JVM语言编写的类
 */
val String.extensionProperty
    get() = "I am a extension property"

/**
 * 为String类添加一个扩展函数extensionMethod(),这个String类可以是Kotlin、Java甚至其他任何JVM语言编写的类
 */
fun String.extensionMethod() {
    println("I am a extension method")
}


/**
 * 使用扩展函数
 */
fun useExtensionMethod() {
    val a = "string"
    a.extensionProperty
    a.extensionMethod()
}

2.4 局部函数

好的代码设计就是尽量减少重复代码,很多时候我们会把重复的代码提取成一个函数进行调用,Kotlin更进一步,它允许你在函数的内部定义函数进行调用。

/**
 * 为了代码的简洁性,我们还可以定义局部函数
 */
fun outerMethod() {
    fun innerMethod() {

    }
}

三 类、对象和接口

Kotlin中类使用class关键声明、接口使用Interface声明,使用冒号(:)代替了extends与implements关键字,可以实现多个接口,但是只能继承一个类。

Kotlin中也有和Java中相似的类与接口的概念,但是有所区别🤔

访问修饰符

可见性

open class BaseClass {

    open fun extendClass() {

    }
}

interface BaseInterface {

    val name: String

    fun implementInterface()
}

class Clasz(override val name: String) : BaseClass(), BaseInterface {

    override fun extendClass() {
        println("I override the BaseClass")
    }


    override fun implementInterface() {
        println("I implment the BaseInterface")
    }
}

构造方法

Kotlin里的构造方法分为主构造函数和从构造方法,主构造函数是类头的一部分,它跟在类名后,类初始化的代码可以放在init代码块里完成。Kotlin也支持参数默认值,所以我们无需像Java那样提供多个重载的构造方法来提供参数默认值。

class Clasz(override val name: String){

    /**************************************** 构造方法与初始化 *********************************************/

    init {
        //TODO 初始化类,在类创建时被调用
    }

    /**
     * 从构造函数
     *
     * 如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别
     * 的次构造函数间接委托。委托到同一个类的另一个构造函数用 this 关键字即可
     */
    constructor(name: String, age: Int) : this(name) {
        println("I am a secondary constructor")
    }
}

3.1 嵌套类与内部类

嵌套的类默认不是内部类,不持有外部类引用,也无法使用外部类成员变量,如果想定义成内部类,需要用关键字inner声明,内部类是持有外部类引用的,可以使用
外部类成员变量。

class Clasz {

    private var outerProperty = "I am a outer property"

    /**************************************** 嵌套类与内部类 ***********************************************/

    /**
     * 嵌套类,不持有外部类引用,也无法使用外部类成员变量
     */
    class nestClass() {

        fun nestMethod() {
        }
    }

    /**
     * 内部类,持有外部类引用,可以使用外部类成员变量
     */
    inner class innerClass() {

        fun nestMethod() {
            outerProperty = "I cam use outerProperty"
        }
    }
}

3.2 数据类

在Java中我们会用到协议只保存数据的类,我们呀为它们写大量重复的方法,在Kotlin只需要用用data关键字标记。

data class Model(val name: String)

就这么简单的一行代码,你将得到:

为了保证数据类的一致性,数据类必须满足:

3.3 object对象

Kotlin没有static的概念,但是提供了object关键字,它代表了在创建一个类的同时并提供一个对象。

object关键字通常用在:

object Single {

}
/**
 * 匿名内部类
 */
view.setOnClickListener(object : View.OnClickListener{
        override fun onClick(v: View?) {
        }
    })

另外还有一种companion object(伴生对象),伴生对象是一种声明在类中的普通对象,它也可以有自己的名字,实现一个接口或者有扩展函数和属性。

Kotlin的伴生对象会被编译成常见静态字段,可以通过ClassName.Companion来访问它,如果伴生对象有名字,则用这个名字替换掉Companion。

class Singleton {

    /**
     * 单例
     */
    companion object {
        val instance by lazy { Singleton() }
    }
}

四 项目实践

如果你还没有尝试过Kotlin,Android Stduio 3.0已经正式支持Kotlin,如果你是第一次接触Kotlin,你可以看一下下面3篇文章,跑一下Demo,体会一下Kotlin。

4.1 findViewById

利用kotlin-android-extensions,我们可以直接使用xml文件里的文件id,和findViewById说再见。

注:目前只能在Activity、Fragment、Adapter上使用。

依赖配置

apply plugin: 'kotlin-android-extensions'

导入属性

// 使用来自主代码集的 R.layout.activity_main
import kotlinx.android.synthetic.main.activity_main.*

// 如果是Adapter则在后面多加个View R.layout.adapter_layout
import kotlinx.android.synthetic.main.adapter_layout.view.*

class MyActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        textView.setText("Hello, world!")
        // 而不是 findViewById(R.id.textView) as TextView
    }
}

更多细节:https://kotlinlang.org/docs/tutorials/android-plugin.html

4.2 Anko

Anke是一个Android开发的工具库,它可以通过Anko DSL代码书写代替XML来进行UI布局。这是一个非常有效的特性,当然它也有许多有用的functions,可以大大提升我们的开发效率。

例如:

更多细节:https://github.com/Kotlin/anko

以上就是本篇文章上半部分的全部内容,下半部分我们会来深入探讨泛型、注解、反射、Kotlin类型系统、Lambda与高阶函数以及DSL构建等方面的内容。

--------------------------------------我是Kotlin项目的分割线😆------------------------------------------

项目地址:https://github.com/guoxiaoxing/phoenix

logo.png Phoenix

功能介绍

play_1.gif
play_2.gif

</p>

功能

<p align="center">

function_1.png function_2.png function_3.png function_4.png

</p>

主题

<p align="center">

theme_default.png theme_red.png theme_orange.png theme_blue.png

</p>

快递开始

添加依赖

在项目根目录build.gradle文件里添加

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

添加依赖

//图片/视频选择、拍照、图片/视频预览
compile 'com.github.guoxiaoxing.phoenix:phoenix-ui:0.0.11'

//选填 - 图片压缩,开启功能:Phoenix.with().enableCompress(true),获取结果:MediaEntity.getCompressPath()
compile 'com.github.guoxiaoxing.phoenix:phoenix-compress-picture:0.0.11'

//选填 - 视频压缩,开启功能:Phoenix.with().enableCompress(true),获取结果:MediaEntity.getCompressPath()
compile 'com.github.guoxiaoxing.phoenix:phoenix-compress-video:0.0.11'

调用功能

Phoenix.with()
        .theme(PhoenixOption.THEME_DEFAULT)// 主题
        .fileType(MimeType.ofAll())//显示的文件类型图片、视频、图片和视频
        .maxPickNumber(10)// 最大选择数量
        .minPickNumber(0)// 最小选择数量
        .spanCount(4)// 每行显示个数
        .pickMode(PhoenixConstant.MULTIPLE)// 多选/单选
        .enablePreview(true)// 是否开启预览
        .enableCamera(true)// 是否开启拍照
        .enableAnimation(true)// 选择界面图片点击效果
        .enableCompress(true)// 是否开启压缩
        .thumbnailHeight(160)// 选择界面图片高度
        .thumbnailWidth(160)// 选择界面图片宽度
        .enableClickSound(true)//ƒ 是否开启点击声音
        .pickedMediaList(pickList)// 已选图片数据
        .videoSecond(0)//显示多少秒以内的视频
        .onPickerListener(new OnPickerListener() {
            @Override
            public void onPickSuccess(List<MediaEntity> pickList) {
                adapter.setList(pickList);
                adapter.notifyDataSetChanged();
            }

            @Override
            public void onPickFailed(String errorMessage) {

            }
        }).start(MainActivity.this, PhoenixOption.TYPE_PICK_MEDIA);

最后的start()方法用来完成启动某项功能,根据type不同启动不同的功能,具体含义如下:

//功能 - 选择图片/视频/音频
public static final int TYPE_PICK_MEDIA = 0x000001;
//功能 - 拍照
public static final int TYPE_TAKE_PICTURE = 0x000002;
//功能 - 预览
public static final int TYPE_BROWSER_PICTURE = 0x000003;

更新日志

贡献代码

欢迎加入改进本项目

License

Copyright 2017 Guoxiaoxing

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
上一篇 下一篇

猜你喜欢

热点阅读