Kotlin for Android (一)

介绍Kotlin

  • 编写代码量少
  • 更加安全:Kotlin编译时期就处理了各种null的情况,避免了执行时异常。如果一个对象可以是null,则我们需要明确地指定它,然后在使用它之前检查它是否是null
  • 它是函数式的:Kotlin是基于面向对象的语言,它使用了很多函数式编程的概念,比如,使用lambda表达式来更方便地解决问题。其中一个很棒的特性就是Collections的处理方式
  • 它可以扩展函数:可以扩展类的更多的特性,甚至我们没有权限去访问这个类中的代码
  • 它是高度互操作性的:你可以继续使用所有的你用Java写的代码和库,因为两个语言之间的互操作性是完美的。可以在一个项目中使用Kotlin和Java两种语言混合编程

特性

Expresiveness 可读性
  • POJO

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Artist {
    private long id;
    private String name;
    private String url;
    private String mbid;
    public long getId() {
    return id;
    }
    public void setId(long id) {
    this.id = id;
    }
    ···
  • Kotlin中创建数据类Artist.kt,自动生成所有属性和访问器

    1
    data class Artist(var id: Long, var name: String, var url: String, var mbid: String)
空安全
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 //编译不通过,非空类不能为null
var artist:Artist=null

//安全调用操作符? 明确地指定一个对象是否能为空
var artist: Artist? = null

// 无法编译, artist可能是null,需要进行处理
// artist.hashCode()

//在artist!=null时调用
artist?.hashCode()

// 判空
if(artist!=null){
artist.hashCode()
}

//给定在null时的替代者
val name=artist?.name?:"empty"

//确保artist不是null的情况下调用,否在抛异常KotlinNullPointerException
artist!!.hashCode()
扩展方法
1
2
3
4
5
6
7
val fragment= Fragment()
fragment.toast("Hello")


fun Fragment.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this@MainActivity, message, duration).show()
}
Lambda
1
2
3
4
5
textView.setOnClickListener { toast("Kotlin") }
private fun toast(text: String) {
val fragment = Fragment()
fragment.toast(text)
}

创建一个项目

  • Project/build.gradle

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    buildscript {
    ext.kotlin_version = '1.2.30'
    ext.support_version = '26.1.0'
    ext.anko_version = '0.10.4'
    repositories {
    google()
    jcenter()
    }
    dependencies {
    classpath 'com.android.tools.build:gradle:3.0.1'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
    }
    }

    allprojects {
    repositories {
    google()
    jcenter()
    }
    }

    task clean(type: Delete) {
    delete rootProject.buildDir
    }
  • app/build.gradle

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-android-extensions'

    android {
    compileSdkVersion 26
    defaultConfig {
    applicationId "com.willkernel.app.kotlindemo"
    minSdkVersion 23
    targetSdkVersion 26
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
    release {
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
    }

    androidExtensions {
    experimental = true
    }
    }

    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation "org.jetbrains.anko:anko-common:$anko_version"
    implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
    implementation "org.jetbrains.anko:anko-coroutines:$anko_version"
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    }
  • 转换java到kotlin

  • 导入布局属性import kotlinx.android.synthetic.main.activity_main.*

    1
    2
    textView.setText(R.string.app_name)
    textView.text = getString(R.string.app_name)
  • 定义类,有一个默认唯一的构造器,如果没有方法,只要名称,变量,可以省略大括号

    1
    2
    3
    4
    5
    6
    class Person(var name: String, var username: String) {
    /**构造函数函数体*/
    init {

    }
    }
  • 任何类继承自Any(类似Object)可以继承其他类,所有类默认不可继承(final),所有只能继承那些声明open或者abstract的类,指定父类中的参数

    1
    2
    open class Animal(name: String)
    class Person(name: String, surname: String) : Animal(name)

函数

  • fun 关键字定义函数,没有指定返回值时,返回Unit类似void,但是Unit是一个对象

    1
    2
    3
    4
    5
    6
    7
    8
    fun onCreate(savedInstanceState: Bundle?) {
    }

    fun add(x: Int, y: Int) : Int {
    return x + y
    }

    fun add(x: Int,y: Int) : Int = x + y
  • 构造方法和函数参数

    • 指定参数默认值

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

      避免重载函数,第二个参数可不传
      toast("Hello")
      toast("Hello", Toast.LENGTH_LONG)
    • 三个参数,避免重载函数,string引用模板[$className] $message,${user.name}

    1
    2
    3
    4
    5
    6
    7
    private fun toast(message: CharSequence, tag: String = "MainActivity", duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, "[$tag] $message", duration).show()
    }

    toast("Hello")
    toast("Hello", "MyTag")
    toast("Hello", "MyTag", Toast.LENGTH_SHORT)

创建一个layout

  • 列表使用recyclerview

    1
    2
    3
    4
    5
    6
    implementation "com.android.support:recyclerview-v7:$support_version"

    val forcastList = findViewById(R.id.recyclerView) as RecyclerView

    //通过属性赋值的方式设置LayoutManager(对象实例化没有使用new关键字),而不是通过setter
    forcastList.layoutManager = LinearLayoutManager(this)
  • Recycler Adapter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class ForcastListAdapter(val items: List<String>) : RecyclerView.Adapter<ForcastListAdapter.VH>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
    return VH(TextView(parent.context))
    }

    override fun getItemCount(): Int = items.size


    override fun onBindViewHolder(holder: VH, position: Int) {
    holder.textView.text = items[position]
    }

    class VH(val textView: TextView) : RecyclerView.ViewHolder(textView)
  • 创建数据,设置Adapter,listOf 创建一个常量的List,它接收一个任何类型的 vararg (可变长的参数),它会自动推断出结果的类型。还有很多其它的函数可以选择,比如 setOf , arrayListOf 或者 hashSetOf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //val forecastList = findViewById<RecyclerView>(R.id.recyclerView)
    recyclerView.layoutManager = LinearLayoutManager(this)
    val items = listOf("Mon 6/23 - Sunny - 31/17",
    "Tue 6/24 - Foggy - 21/8",
    "Wed 6/25 - Cloudy - 22/17",
    "Thurs 6/26 - Rainy - 18/11",
    "Fri 6/27 - Foggy - 21/10",
    "Sat 6/28 - TRAPPED IN WEATHERSTATION - 23/18",
    "Sun 6/29 - Sunny - 20/7")
    recyclerView.adapter = ForcastListAdapter(items)

变量和属性

基本类型
  • 数字类型不会自动转型,做一个明确的类型转换

    1
    2
    3
    4
    5
    val i:Int=7;
    val d:Double=i.toDouble()
    Log.e(tag, d.toString())

    E/MainActivity: 7.0
  • 字符char的数字值不能直接使用,需要转换为一个数字

    1
    2
    3
    4
    5
    val c='c'
    val i2:Int=c.toInt()
    Log.e(tag,i2.toString())

    E/MainActivity: 99
  • 仅适用Int Long的按位运算,包括

    1
    2
    3
    4
    5
    6
    7
    shl(bits) – signed shift left (Java's <<)
    shr(bits) – signed shift right (Java's >>)
    ushr(bits) – unsigned shift right (Java's >>>)
    and(bits) – bitwise and
    or(bits) – bitwise or
    xor(bits) – bitwise xor
    inv() – bitwise inversion
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      val FLAG1 = 8L
      val FLAG2 = 16L
      //按位运算,Int Long可用
      val bitwiseOr = FLAG1 or FLAG2
      val bitwiseAnd = FLAG1 and FLAG2
      Log.e(tag, bitwiseOr.toString())
      Log.e(tag, bitwiseAnd.toString())
      E/MainActivity: 24
      E/MainActivity: 0
  • string字符串的访问

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //string 可以像数组一样访问
    val s = "Example"
    val c1 = s[2]
    Log.e(tag, c1.toString())
    for (c in s) {
    Log.e(tag, "item =$c")
    }

    E/MainActivity: a
    E/MainActivity: item =E
    E/MainActivity: item =x
    E/MainActivity: item =a
    E/MainActivity: item =m
    E/MainActivity: item =p
    E/MainActivity: item =l
    E/MainActivity: item =e
变量
  • 变量可以很简单地定义成可变( var )和不可变( val )的变量,不可变对象也可以说是线程安全的,因为它们无法去改变,也不需要去定义访问控制,因为所有线程访问到的对象都是同一个,尽量使用val,自动从赋值语句判断变量类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    val actionBar = supportActionBar
    Log.e(tag, actionBar.toString())
    val a: Any = 23
    Log.e(tag, a.toString())

    val c: Context = this
    Log.e(tag, c.toString())

    E/MainActivity: android.support.v7.app.WindowDecorActionBar@2511c44
    E/MainActivity: 23
    E/MainActivity: com.willkernel.app.kotlindemo.MainActivity@b16dc5a
属性
  • 当操作Java代码的时候,Kotlin将允许使用属性的语法去访问在Java文件中定义的getter/setter方法。编译器会直接链接到它原始的getter/setter方法。所以当我们直接访问属性的时候不会有性能开销
  • 在getter和setter中访问这个属性自身的值,它需要创建一个 backingfield 。可以使用 field 这个预留字段来访问,它会被编译器找到正在使用的并自动创建。需要注意的是,如果我们直接调用了属性,那我们会使用setter和getter而不是直接访问这个属性。 backing field 只能在属性访问器内访问
    1
    2
    3
    4
    5
    var nick: String = ""
    get() = field.toUpperCase()
    set(value) {
    field = "Name: $nick"
    }
willkernel wechat
关注微信公众号
帅哥美女们,请赐予我力量吧!