kotlin instead of findViewById method

After using Kotlin in the Android project, I found that Kotlin is quite powerful. You can introduce layout and use controls directly without findViewById

Method 1: Use the kotlin plugin to automatically generate

Introduce kotlin extension plugin

apply plugin: ‘kotlin-android-extensions’

Import related layout files automatically generated by kotlin

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

Above, the kotlin plugin will generate the package corresponding to the layout file of the current project. After importing it, you can directly use the Id of the layout file as a control, and you don’t need findViewById at all.

<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"//直接使用
}

It is worth noting that the above is used in the Activity, and the use in the Fragment must obtain the control in the onViewCreated method, otherwise it will be a null pointer

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 = "测试"
}

Method 2: Use annotations (only for learning annotations, recommended method 1)

Android children's shoes who have used frameworks such as butterknife should know that they use annotations instead of findViewById, which saves a lot of cumbersome and inelegant code. Let me introduce how to use Kotlin annotations to implement such a simple function.

1. Kotlin declaration annotations need to use the annotation keyword in front of the class

annotation class BindView() {}

2. We all know that annotation classes generally need to specify the annotation target type (Target), annotation visibility (Retention), etc. (kotlin annotations are fully compatible with java, children’s shoes that don’t understand, please learn about java annotations first), so, there are as follows:

@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class BindView(val id:Int = -1) {}

Briefly explain the meaning:

  • @Target specifies the type of element (class, function, attribute, expression, etc.) that can be annotated with this annotation, here specified as attribute (FIELD);
  • @Retention is used to define at which level the annotation is available, in the source code (SOURCE) , in the class file (CLASS) or at runtime (RUNTIME) . ;

In the constructor of the annotation class above, we passed an id, which is the id of the control, to bind the control

3. Using reflection injection

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. Use in 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)
}

How to use it in Fragment? It is similar to Activity, except that one more contentView parameter needs to be passed in, because the creation of Fragment view is special

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()
        }
 }

In Fragment:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        mContentView = inflater.inflate(contentView, container, false)

        //注解绑定视图id
        bindView(this,mContentView)

        return mContentView
  }

Guess you like

Origin blog.csdn.net/gs12software/article/details/103369368