kotlin代替findViewById的方法

在安卓项目使用了Kotlin之后,发现Kotlin一个相当强大的地方,可以不用findViewById,引入布局,直接使用控件

方式一:使用kotlin插件自动生成

引入kotlin扩展插件

apply plugin: ‘kotlin-android-extensions’

引入kotlin自动生成的相关布局文件

import kotlinx.android.synthetic.main.activity_main.*

上面,kotlin插件会生成当前项目的布局文件对应的包,引入进来后,直接用布局文件的Id当作控件使用即可,根本不需要findViewById

<TextView
     android:id="@+id/tv_data"
     android:layout_width="match_parent"
     android:layout_height="0dp"
     android:layout_weight="1"
     android:gravity="center" />
 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) {}

简单说明一下含义:

  • @Target 指定可以用该注解标注的元素的类型(类、函数、属性、表达式等),这里指定为属性(FIELD);
  • @Retention 用来定义该注解在哪一个级别可用,在源代码中(SOURCE)类文件中(CLASS)或者运行时(RUNTIME)。;

在上面这个注解类的构造函数我们传入了一个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
  }

猜你喜欢

转载自blog.csdn.net/gs12software/article/details/103369368