13.6 配置 Kotlin 与 Anko 依赖

我们默认生成的 app 项目的 Gradle 配置文件build.gradle如下:

  1. apply plugin: 'com.android.application'
  2. android {
  3. compileSdkVersion 25
  4. buildToolsVersion "25.0.3"
  5. defaultConfig {
  6. applicationId "com.easy.kotlin.mytodoapplication"
  7. minSdkVersion 21
  8. targetSdkVersion 25
  9. versionCode 1
  10. versionName "1.0"
  11. testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  12. }
  13. buildTypes {
  14. release {
  15. minifyEnabled false
  16. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  17. }
  18. }
  19. }
  20. dependencies {
  21. compile fileTree(dir: 'libs', include: ['*.jar'])
  22. androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
  23. exclude group: 'com.android.support', module: 'support-annotations'
  24. })
  25. compile 'com.android.support:appcompat-v7:25.3.1'
  26. compile 'com.android.support.constraint:constraint-layout:1.0.2'
  27. compile 'com.android.support:design:25.3.1'
  28. testCompile 'junit:junit:4.12'
  29. }

下面我们在 app 项目的build.gradle里面加上Kotlin 、Anko 、Realm、Butter Knife 等依赖。

13.6.1 Kotlin依赖

首先,启用插件kotlin-android :

  1. apply plugin: 'kotlin-android'

然后,添加构建脚本

  1. buildscript {
  2. }

我们使用 Kotlin 1.1.3版本。在构建脚本中添加kotlin-gradle-plugin依赖,使用 Kotlin 对应的版本号。

  1. buildscript {
  2. ext.kotlin_version = '1.1.3'
  3. repositories {
  4. mavenCentral()
  5. }
  6. dependencies {
  7. classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  8. }
  9. }

在项目依赖里添加 Kotlin 标准库:

  1. // Kotlin
  2. compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

13.6.2 添加 Kotlin 源代码目录

首先,我们在 src/main/下面新建一个 kotlin 目录,来存放 Kotlin源码。然后在 build.gradle 文件里的 android {} 配置里面添加Java的编译路径:

  1. android {
  2. ...
  3. sourceSets {
  4. // += , 在main中创建kotlin文件夹, 用于存放kotlin代码
  5. main.java.srcDirs += 'src/main/kotlin'
  6. }
  7. }

刚添加完毕,src/main/kotlin 还没有变成源码目录的蓝色,这个时候点击下图右上角的 Sync Now :

Kotlin极简教程

Gradle 同步完毕,即可看到kotlin 目录已经变成蓝色的源码目录了:

Kotlin极简教程

13.6.3 Anko依赖

在项目依赖里添加

  1. // Anko
  2. compile 'org.jetbrains.anko:anko-sdk15:0.8.2' // sdk19, sdk21, sdk23 are also available
  3. compile 'org.jetbrains.anko:anko-support-v4:0.8.2' // In case you need support-v4 bindings
  4. compile 'org.jetbrains.anko:anko-appcompat-v7:0.8.2' // For appcompat-v7 bindings

13.6.4 Realm依赖

  1. compile 'io.realm:realm-android:0.87.1'
  2. compile 'com.github.thorbenprimke:realm-recyclerview:0.9.12' // 在jitpack.io上

其中,Realm是一个轻量级的跨平台移动数据库引。Realm 简单易用,model 设计在代码中,更加易于维护,同时其性能也不错。在Android开发中,它可以替代 SQLite 和 ORM 框架。相比SQLite,Realm更快并且具有很多现代数据库的特性,比如支持JSON,流式api,数据变更通知,以及加密支持。

RecyclerView用于在有限的窗口展现大量的数据,相比ListView、GridView,RecyclerView标准化了ViewHolder,而且更加灵活,可以轻松实现ListView实现不了的样式和功能。我们使用的com.github.thorbenprimke:realm-recyclerview 依赖包在在jitpack.io上, 所以我们还需要配置一下仓库地址:

  1. repositories {
  2. mavenCentral()
  3. maven { url "https://jitpack.io" }
  4. }

提示:realm-recyclerview的 Github 地址

另外, Kotlin使用 Realm 还要加上注解处理的依赖库:

  1. // kotlin使用realm的注解处理依赖库
  2. kapt "io.realm:realm-annotations:0.87.1"
  3. kapt "io.realm:realm-annotations-processor:0.87.1"

13.6.5 Butter Knife依赖

Butter Knife是基于注解处理方式工作:通过对代码注解自动生成模板代码。我们添加其依赖如下:

  1. // Butter Knife,专门为Android View设计的绑定注解,专业解决各种findViewById
  2. compile 'com.jakewharton:butterknife:8.7.0'
  3. annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0'

Butter Knife主要是用来做Android视图的成员变量和属性的数据绑定。在开发过程中,我们通常要写大量的findViewById和点击事件,像初始view、设置view监听这样简单而重复的操作会显得比较繁琐。而我们有了 Butter Knife,就可以通过使用注解直接生成样板代码。例如,在 Java 中我们可以通过在字段上使用 @BindView 来替代 findViewById 的调用。上面的配置中的annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0'就是来处理这些注解从而生成样板代码的。

  1. @Bind(R.id.todo_item_todo_title)
  2. public TextView todoTitle;
  3. @Bind(R.id.todo_item_todo_content)
  4. public TextView todoContent;

而在 Kotlin 中使用Butter Knife情况有些不同,需要作额外的配置。

如果在Kotlin中直接使用ButterKnife的注解方式的话,会出现空指针的异常,导致绑定失败。例如

  1. @Bind(R.id.todos_recycler_view)
  2. var realmRecyclerView: RealmRecyclerView? = null

运行会报错:

  1. Caused by: kotlin.KotlinNullPointerException
  2. at com.easy.kotlin.mytodoapplication.TodoListFragment.onResume(TodoListFragment.kt:43)

一般情况下,我们使用Kotlin集成 Java 生态的一些框架的时候,像 Spring Boot,JPA,Butter Knife,Realm等,都需要一些额外的插件或者依赖来“填充缝隙”(例如:all-open, kotterknife,realm-annotations等), 所谓Kotlin 与 Java 的无缝集成,很多时候并非Java 中怎么用,Kotlin就直接拿过来就怎么用,往往是要再添加一些插件或者额外的配置等。

那么要如何才能在Kotlin的环境中使用ButterKnife呢?

在早些时候,ButterKnife的作者已经帮我们想好解决方案了,那就是——KotterKnife,见名知意。KotterKnife的GitHub地址 。这个插件是建立在ButterKnife 7的基础上的。

下面我们配置一下在 Kotlin 中使用 Butter Knife 的依赖库 KotterKnife。

首先在repositories中添加KotterKnife的仓库地址(KotterKnife不在 Maven Center 仓库中,而是在oss.sonatype.org仓库中。这么多仓库,要是哪天能统一用一个就方便多了)。

  1. repositories {
  2. ...
  3. maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
  4. }

然后在dependencies里面添加依赖

  1. dependencies {
  2. ...
  3. compile 'com.jakewharton:butterknife:7.0.1'
  4. compile 'com.jakewharton:kotterknife:0.1.0-SNAPSHOT'
  5. }

采用这种方式的配置,我们的视图注入代码如下

  1. val todoTitle: TextView by bindView(R.id.todo_item_todo_title)
  2. val todoContent: TextView by bindView(R.id.todo_item_todo_content)

这样的代码看起来不是那么的优雅,还没有在 Java 中直接使用注解来的简单好看。同时要注意的是,如果使用 kotterknife 0.1.0 + butterknife:7.0.1 ,同时使用 Java 跟 Kotlin 混合编程的场景中使用 Butter Knife,发现配了KotterKnife 之后的 Java 的注解式写法就失效了。也就是说,如果我们上面添加了KotterKnife的依赖,那么 Java 代码中同时使用 Butter Knife 注解的地方会绑定失败。不过这个问题,在后面的新版本中已经解决。例如在butterknife 8.7.0中,我们可以直接添加下面的依赖项:

  1. compile 'com.jakewharton:butterknife:8.7.0'
  2. annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0'
  3. kapt 'com.jakewharton:butterknife-compiler:8.7.0'

其中,annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0' 是 Java 的butterknife注解处理器。kapt 'com.jakewharton:butterknife-compiler:8.7.0' 是 Kotlin 的butterknife注解处理器(Kotlin Annotation processing tool,kapt)。

这样我们的代码就继续优雅简洁下去了:

  1. @BindView(R.id.todo_item_todo_title)
  2. lateinit var todoTitle: TextView
  3. @BindView(R.id.todo_item_todo_content)
  4. lateinit var todoContent: TextView

其中,lateinit 修饰符允许声明非空类型,并在对象创建后(构造函数调用后)初始化。 不使用 lateinit 则需要声明可空类型并且有额外的空安全检测操作。

当然,我们使用 Butter Knife 的同时,仍然可以使用原生的 findViewById :

  1. class MainActivity : AppCompatActivity() {
  2. var fab: FloatingActionButton? = null
  3. override fun onCreate(savedInstanceState: Bundle?) {
  4. super.onCreate(savedInstanceState)
  5. setContentView(R.layout.activity_main)
  6. val toolbar = findViewById(R.id.toolbar) as Toolbar
  7. setSupportActionBar(toolbar)
  8. fab = findViewById(R.id.fab) as FloatingActionButton
  9. // 添加日程事件
  10. fab?.setOnClickListener { _ ->
  11. ...
  12. hideFab()
  13. }
  14. }
  15. fun hideFab() {
  16. fab?.visibility = View.GONE
  17. }
  18. fun showFab() {
  19. fab?.visibility = View.VISIBLE
  20. }
  21. }