在安卓項目使用了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
}