“¡Déjenla aquí, que se muera sola!” dijeron ellos, abandonando a la abuela en la nieve. Los canallas no sabían que el boomerang pronto volvería.
Valentina López caminaba hacia su portal. Las señoras del banco charlaban sobre el flamante coche aparcado cerca.
¿De quién es? preguntó Valentina.
¡Ni idea! contestó una. Seguro que es de María. Aquí los abuelos no llegan con coches tan caros.
¡Aquí solo vienen las ambulancias! añadió otra.
Las vecinas siguieron cotorreando sobre políticos y chismes. De pronto, apareció la tal María, dueña del coche. Salió sin mirar a nadie ni al vehículo sobre el césped. Valentina entró rápido en casa.
¿Valentina López? dijo un hombre en el rellano. ¿Se acuerda de mí? Hablamos hace unos días. Soy su sobrino.
¡Ah, Javier! exclamó ella. ¿Por qué no avisaste de tu visita? ¿Ese coche es tuyo?
Sí, mío.
¡Pues ve a moverlo antes de que la gente proteste! ¿Cómo se te ocurre aparcar en mis flores?
El sobrino salió pitando. Valentina fue a preparar té. Necesitaba vender el piso; no quería dejar el césped destrozado a las vecinas.
Hacía años, su tío visitaba con su hijo. Luego, la familia perdió contacto. Y ahora, ¡el chico aparecía! Algo en él despertaba sospechas en Valentina: fumaba demasiado. Joven, pero con los dientes amarillos. Menos mal que vino. Ella no quería contratar a un agente inmobiliario; prefería darle algo al sobrino. Pero él rechazó el dinero.
Viuda y sin hijos, Valentina ansiaba mudarse al campo. Mejor aire puro que bajar cuatro pisos cada día. En el pueblo había huerto. Mientras tuviera fuerzas, quería plantar verduras. En otoño apareció un comprador.
Mañana empieza el invierno. Mejor vendemos en primavera decidió Valentina, posponiendo la venta.
¡Pero en primavera subirán los precios! replicó Javier. Con frío se revisa mejor la calefacción. Además, ya hay comprador. ¿Y si luego se e# Kotlin Android Extensions
## 1. 概述
相信每一位安卓开发人员对findViewById() 这个方法再熟悉不过了,毫无疑问,潜在的 bug 和脏乱的代码令后续开发无从下手的尽管存在一系列的开源库能够为这个问题带来解决方案,然而对于运行时依赖的库,需要为每一个 View 注解变量字段
现在 Kotlin 安卓扩展插件能够提供与这些开源库功能相同的体验,不需要添加任何额外代码,也不影响任何运行时体验
因此,我们可以写出如下代码:
“`kotlin
// 使用来自主代码集的 R.layout.activity_main
import kotlinx.android.synthetic.main.activity_main.*
class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 代替 findView(R.id.textView)
textView.setText(“Hello, world!”)
// 而不是 findViewById(R.id.textView) as TextView
textView.text = “Hello, world!”
}
}
“`
textView 是对 Activity 的一项扩展属性,与在 activity_main.xml 中的声明具有同样类型 (它是一个 TextView)
## 2. 使用 Kotlin 安卓扩展
### 2.1 依赖配置
在该教程中我们会提供为 Gradle 构建安卓工程的配置方法
#### 2.1.1 用于安卓应用的配置
安卓扩展是 Kotlin IDEA 插件的组成之一,因此不需要再单独安装新插件
开发者仅需要模块的 `build.gradle` 文件中启用安卓扩展 Gradle 插件即可:
“`groovy
apply plugin: ‘kotlin-android-extensions’
“`
#### 2.1.2 用于安卓 library 的配置
安卓 library 内部模块的使用略有不同 (参考[多模块项目](#3-多模块项目)),因此该 library 产生的插件 artifact 不是基于 `R` 类的,而是基于随该安卓 library 编译产生的包名
因此开发者需要在主模块的 `build.gradle` 文件中添加以下代码来读取该包名:
“`groovy
androidExtensions {
experimental = true
}
“`
一旦开启该选项,该安卓扩展就会尝试读取该包名每个未开启该选项的模块都会在 logcat 中看到以下错误信息:
“`
Could not read package name from android manifest
“`
### 2.2 导入 synthetic 属性
仅需要一行即可非常方便导入指定布局文件中所有控件属性:
“`kotlin
import kotlinx.android.synthetic.main.<布局>.*
“`
假设当前布局文件是 `activity_main.xml`,我们只需要引入 `kotlinx.android.synthetic.main.activity_main.*`
若需要调用 View 的合成属性(在适配器内等),同时还应该导入 `kotlinx.android.synthetic.main.activity_main.view.*`
一旦我们声明了以上导入,我们即可在代码中调用对应 View 组件,这些组件都是布局中的 View 的引用比如下例这个布局文件:
“`xml
“`
将有一个名为 `hello` 的属性:
“`kotlin
activity.hello.text = “Hello World!”
“`
### 2.3 混淆
安卓扩展不需要依赖额外的依赖库由插件在模块中生成代码,仅对每个 kotlin 类进行一次视图缓存
因此在混淆项目中需要开启以下混淆规则:
“`
-keep class kotlinx.android.synthetic.** { *; }
“`
> **注意**: 如果代码中引用到的 View 在 XML 布局中没有声明,混淆后运行会发生 crash
## 3. 多模块项目
当我们在多模块项目中使用 Kotlin 安卓扩展时,有几个小问题需要注意
### 3.1 应用模块中的常规布局
假设有一个应用模块使用了一个 library 模块,比如:
`build.gradle` 在应用模块中配置如下:
“`groovy
apply plugin: ‘com.android.application’
apply plugin: ‘kotlin-android’
apply plugin: ‘kotlin-android-extensions’
“`
然后我们在应用模块的布局中添加一个 `android:id=”@+id/hello”` 的 TextView
这时候就可以在应用模块中调用该 TextView 了:
“`kotlin
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
hello.text = “Hello World!”
}
}
“`
### 3.2 library 模块中的常规布局
假设我们在 library 模块中也添加了一个同样的布局,这时候如果我们尝试在 library 模块中调用该 TextView,IDE 就会提示该属性未定义
这时候我们需要打开模块的 `build.gradle` 配置文件,并添加 `androidExtensions.experimental = true` 的配置:
“`groovy
apply plugin: ‘com.android.library’
apply plugin: ‘kotlin-android’
apply plugin: ‘kotlin-android-extensions’
androidExtensions {
experimental = true
}
“`
然后在代码中我们就可以正常使用该属性了:
“`kotlin
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
hello.text = “Hello World!”
}
}
“`
### 3.3 应用模块中调用 library 模块中的布局
假设我们在 library 模块中声明了一个布局文件,并且希望在应用模块中调用该布局文件中的 View,这时候我们需要在应用模块中这样导入布局:
“`kotlin
import kotlinx.android.synthetic.main.activity_main.view.*
“`
然后我们就可以在代码中这样使用该布局文件中的 View 了:
“`kotlin
val view = LayoutInflater.from(this).inflate(R.layout.activity_main, null)
view.hello.text = “Hello World!”
“`
## 4. 注意事项
### 4.1 关于 `LayoutContainer` 的支持
在安卓扩展中,通常情况下的 `Activity“Fragment` 和 `View` 都默认实现了 `LayoutContainer` 接口但是在某些版本中,`View` 并不支持该接口,这时候我们只能通过导入 `kotlinx.android.synthetic.main.activity_main.view.*` 来使用相关属性
### 4.2 关于 `View` 的缓存
安卓扩展会对每个 `Activity“Fragment` 和 `View` 的 `findViewById` 的结果进行缓存,以避免重复调用 `findViewById`
但是这些缓存并不会在 `Activity“Fragment` 和 `View` 的生命周期结束后自动清除,因此如果我们在 `Activity“Fragment` 和 `View` 的生命







