在安卓项目使用了Kotlin之后,发现Kotlin一个相当强大的地方,可以不用findViewById,引入布局,直接使用控件
方式一:使用kotlin插件自动生成
引入kotlin扩展插件
apply plugin: ‘kotlin-android-extensions’
引入kotlin自动生成的相关布局文件
import kotlinx.android.synthetic.main.activity_main.*
上面,kotlin插件会生成当前项目的布局文件对应的包,引入进来后,直接用布局文件的Id当作控件使用即可,根本不需要findViewById
override fun onCreate(savedInstanceState: Bundle?): View? {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tv_data.text = "test"//直接使用
}
值得注意的是,以上是在Activity里面使用,在Fragment里面使用必须在onViewCreated方法获取控件,不然会空指针
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
mContentView = inflater.inflate(layoutId, container, false)
return mContentView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
tv_text.text = "测试"
}
方式二:利用注解(仅供学习注解使用,推荐方式一)
用过butterknife之类框架的安卓童鞋都应该清楚,它们使用了注解来代替findViewById,从而少了一堆繁琐又不优雅的代码。下面我就介绍下如何利用Kotlin注解实现这样一个简易的功能。
1.kotlin声明注解需要在类前面使用 annotation 关键字
annotation class BindView() {}
2.我们都知道,注解类一般需要指定注解目标类型(Target),注解的可见性(Retention)等(kotlin注解与java完全兼容,不懂的童鞋请先去了解java注解),于是,就有了如下:
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class BindView(val id:Int = -1) {}
简单说明一下含义:
在上面这个注解类的构造函数我们传入了一个id,即控件的id,用于绑定该控件
3.利用反射注入
fun bindView(activity: Activity) {
try {
//通过反射获取当前Activity的所有属性
val fields = activity.javaClass.declaredFields
for (field in fields) {
//判断该属性是否使用自己定义的BindView注解
if (field.isAnnotationPresent(BindView::class.java)) {
val inject = field.getAnnotation(BindView::class.java) //获得该类上注解
val id = inject.id //读取view的id
if (id > 0) {
field.isAccessible = true
field[activity] = activity.findViewById(id) //给view赋值,相当于 view = findviewById(R.id.tv);
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
4.在Activity中使用:
@BindView(R.id.tv_data)
private var tvData: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bindView(this)
}
如何在Fragment中使用呢?同Activity类似,只不过需要多传入一个contentView参数,因为Fragment视图的创建比较特殊
fun bindView(fragment: Fragment, contentView: View) {
try {
val fields = fragment.javaClass.declaredFields
for (field in fields) {
if (field.isAnnotationPresent(BindView::class.java)) {
val inject = field.getAnnotation(BindView::class.java)
val id = inject.id
if (id > 0) {
field.isAccessible = true
field[fragment] = contentView.findViewById(id)
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
在Fragment中:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
mContentView = inflater.inflate(contentView, container, false)
//注解绑定视图id
bindView(this,mContentView)
return mContentView
}