@cxm-2016
2016-08-16T11:34:36.000000Z
字数 2982
阅读 2190
android
作者:陈小默
kotlin版本:1.0.3
Kotlin是一种基于JVM的语言,这种语言最令人着迷的一点的就能与Java语言互相调用。于是有些人就以为按照Java的用法就能写好Koltin的程序。可是,Kotlin毕竟也是一门独立的高级语言呀。其内部一些机制和Java还是有很大区别的。现在我们通过编写一个运行时View注入的例子来了解一下Kotlin的反射。
首先我们先定义一个注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
annotation class BindView(val value: Int)
这里我们首先定义一个类BaseViewHolder,这个类用来处理View的注入事件。因为该类要被继承,所以这里应该加上open关键字。
open class BaseViewHolder {
fun bind(activity: Activity) {
}
}
在类里我们添加了一个bind方法,这个方法的目的是用来实现注入操作,我们最后再来实现这个方法。
接下来我们实现一个MyViewHolder去继承BaseViewHolder
class MyViewHolder : BaseViewHolder() {
@BindView(R.id.name) var name: TextView? = null
@BindView(R.id.phone) var phone: TextView? = null
@BindView(R.id.email) var email: TextView? = null
@BindView(R.id.show) var show: Button? = null
}
布局文件我就不说了,自行创建更改上面的id
接下来在Activity中我们调用注入方法并显示输入信息
class MainActivity : AppCompatActivity() {
private var holder = MyViewHolder()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
holder.bind(this)
holder.show?.setOnClickListener { v ->
Toast.makeText(this@MainActivity,
"""姓名:${holder.name?.text.toString()}
|手机:${holder.phone?.text.toString()}
|邮箱:${holder.email?.text.toString()}""".trimMargin(), Toast.LENGTH_SHORT).show()
}
}
}
好了,现在解释一下上述代码:第10行字符串表示方式是"""原生字符串"""里面的内容可以跨行且不需要转义,第11行和第12行前面的|代表前导字符,表示这一行从这里开始前面的空白字符都忽略不计。
目前我们的整个工程就算写好了。现在我们要回过头去实现注入方法,考虑一下,如果使用Java的反射机制应该如何实现注入。。。。。。嗯,应该是这个样子:
fun bind(activity: Activity) {
val any = this
this.javaClass.declaredFields.forEach { field ->
field.isAccessible = true
val ann = field.getAnnotation(BindView::class.java)
if (ann != null) {
val view = activity.findViewById(ann.value)
field.set(any, view)
}
}
}
我们运行一下试试。。。。。。没反应,对不对。这是因为Kotlin和Java采用的反射机制不同,而我们的MyViewHolder并不是Java文件我们看到的class.java是为了Kotlin能够调用Java文件增加的类信息。但是我们Kotlin文件的交互还要通过Java去进行操作吗?所以我们要使用Kotlin内部的反射机制。
不知道是我没有找到还是因为当前1.0.3版本不完整,我并没有找到属性操作的相关方法,但是我通过查看源码扩展了常用的方法,第一个是任何类型都可以使用的获得Koltin类信息的方法,第二个是获得相应类型的全部注解(java8允许一个属性存在多个同类型的注解)第三个就是我们常用的获得获得属性上一个注解,随后一个方法是扩展的属性set方法
fun Any.kotlin() = this.javaClass.kotlin
fun KProperty1<*, *>.getAnnotations(type: KClass<*>): List<Annotation>? {
return this.annotations.filter { ann ->
ann.annotationClass == type
}.toList()
}
fun <T : Annotation> KProperty1<*, *>.getAnnotation(type: KClass<T>): T? {
val list = getAnnotations(type)
return if (list != null) {
list[0] as T
} else {
null
}
}
fun KProperty1<Any, Any?>.set(any: Any, value: Any?) {
(this as KMutableProperty1<Any, Any?>).set(any, value)
}
将这些扩展置于同级别包下即可
现在我们再来实现注入方法
fun bind(activity: Activity) {
val any = this
any.kotlin().memberProperties.forEach { mem ->
mem.isAccessible = true
val ann = mem.getAnnotation(BindView::class)
if (ann != null) {
val view = activity.findViewById(ann.value)
mem.set(any, view)
}
}
}
现在一个Kotlin的注解就成了
这里有其他更好的方法:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class BindView(val value: Int)
open class BaseViewHolder {
fun bind(activity: Activity) {
val any = this
any.javaClass.declaredFields.forEach { field ->
field.isAccessible = true
val ann = field.getAnnotation(BindView::class.java)
if (ann != null) {
val view = activity.findViewById(ann.value)
field.set(any, view)
}
}
}
}