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 指定該註解是否 存儲在編譯後的 class 文件中,以及它在運行時能否通過反
    射可見 (默認都是 true);

在上面這個註解類的構造函數我們傳入了一個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
  }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章