今天從Google公眾號了解到關(guān)于ViewBinding的相關(guān)文章,趕上手頭項目正在做優(yōu)化欣舵,于是結(jié)合反射封裝了BaseActivity和BaseFragment,記錄下來同時和大家一起探討學(xué)習(xí)缀磕。先簡單介紹下ViewBinding:
從 Android Studio 3.6 開始缘圈,ViewBinding能夠通過生成綁定對象來替代 findViewById,從而可以幫你簡化代碼袜蚕,并且從 findViewById 的模版代碼中解脫出來糟把。ViewBinding的主要優(yōu)點是所有綁定類都是由Gradle插件生成的,因此它對構(gòu)建時間沒有影響牲剃,并且具有編譯時安全性遣疯。
ViewBinding與DataBinding的區(qū)別:
DataBinding 僅處理使用 <layout> 代碼創(chuàng)建的數(shù)據(jù)綁定布局;
ViewBinding 不支持布局變量或布局表達(dá)式凿傅,因此它不能用于在 XML 中將布局與數(shù)據(jù)綁定缠犀。
ViewBinding相較ButterKnife、Kotlin Android Extensions的幾點優(yōu)勢:
目前Android開發(fā)中完成View映射的方法主要有 findViewById狭归、 ButterKnife, 如果使用kotlin的話還可以使用Kotlin Android Extensions夭坪。
偷個懶,直接引用下其它文章中的描述:
Type safety:
findViewById, ButterKnife 均存在類型轉(zhuǎn)換問題过椎,例如不小心將一個TextView錯誤的賦值給一個Button變量,都會報錯戏仓,這一錯誤很容易出現(xiàn)疚宇,關(guān)鍵在錯誤還出現(xiàn)在運行時亡鼠,而不是編譯時!
而ViewBinding中敷待,產(chǎn)生的binding類中的屬性是依據(jù)XML layout文件生成的间涵,所以類型不會錯,生成的時候已經(jīng)處理好了榜揖。
Null safety:
findViewById, ButterKnife與Kotlin Android Extensions 均存在Null不安全問題勾哩。這個什么意思呢?就是在我們訪問那個View的時候它不存在举哟。為什么會出現(xiàn)這種情況呢思劳?例如不小心使用了錯誤的Id,或者訪問的時候那個view還不存在。
使用了錯誤Id這個估計大家都有此類經(jīng)歷妨猩,但是訪問時候那個view不存在怎么理解呢潜叛?例如我們在手機橫屏和豎屏的時候分別使用一套XML layout文件,假設(shè)橫屏中包含了一個豎屏中沒有的view,那么在屏幕從橫屏旋轉(zhuǎn)到豎屏的時候壶硅,NullPointer問題就出現(xiàn)了威兜。
而ViewBinding中, 產(chǎn)生的binding類中的屬性是依據(jù)XML layout文件生成的庐椒,所以Id不會錯椒舵。而且其將僅存在某一個配置下的layout文件的那些view對應(yīng)的字段標(biāo)記為@Nullable,而且约谈,生成類中還會很貼心的給你加上詳細(xì)的注釋逮栅。這一切都是為了提醒程序員,注意對這個view特別處理窗宇,它在某些情況下為Null措伐。
簡潔優(yōu)雅:
將綁定view的模板代碼自動生成到了其他類中,使controlor類(Activity军俊,F(xiàn)ragment)更加清晰了侥加。
原文鏈接:Android開發(fā)之秒懂ViewBinding,一代神器ButterKnife的終結(jié)者
————————————————
ViewBinding的常規(guī)使用:
- 在 build.gradle 中開啟視圖綁定
開啟視圖綁定無須引入額外依賴,從 Android Studio 3.6 開始粪躬,視圖綁定將會內(nèi)建于 Android Gradle 插件中担败。需要打開視圖綁定的話,只需要在 build.gradle 文件中配置 viewBinding 選項:
// 需要 Android Gradle Plugin 3.6.0
android {
viewBinding {
enabled =true
}
}
在 Android Studio 4.0 中镰官,viewBinding 變成屬性被整合到了 buildFeatures 選項中提前,所以配置要改成:
//Android Studio 4.0
android{
buildFeatures{
viewBinding=true
}
}
配置完成后,視圖綁定就會根據(jù)現(xiàn)有的 XML 文件泳唠,為 Module 內(nèi)所有的布局文件生成綁定對象狈网。無須修改原有布局的 XML 文件,視圖綁定將根據(jù)你現(xiàn)有的布局自動完成所有工作。你可以在任何需要填充布局的地方使用綁定對象拓哺,比如 Fragment勇垛、Activity、甚至是 RecyclerView Adapter(或者說是 ViewHolder 中)士鸥。
- 在 Activity 中使用視圖綁定
假如你有一個布局文件名叫 activity_my.xml闲孤,其中包含了一個按鈕和兩個文本視圖。視圖綁定會為這個布局生成一個名叫 ActivityMyBinding 的類烤礁,布局文件中所有擁有 id 的視圖讼积,都會在這個類中有一個對應(yīng)的屬性:
overridefunonCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
val binding = ActivityMyBinding.inflate(layoutInflater)
binding.title.text ="Hello"
binding.subtext.text ="World"
binding.button.setOnClickListener {
/* ... */
}
setContentView(binding.root)
}
- 在 Fragment,RecyclerView Adapter等的使用可參考文章:
[譯]深入研究 ViewBinding 在 < include>, < merge>, adapter, fragment, 和 activity 中使用View Binding
————————————————
BaseActivity的封裝和使用:
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
lateinit var binding: VB
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//利用反射脚仔,調(diào)用指定ViewBinding中的inflate方法填充視圖
val type = javaClass.genericSuperclass
if (type is ParameterizedType) {
val clazz = type.actualTypeArguments[0] as Class<VB>
val method = clazz.getMethod("inflate", LayoutInflater::class.java)
binding = method.invoke(null, layoutInflater) as VB
setContentView(binding.root)
}
init()
}
abstract fun init()
}
此時勤众,我們的Activity的使用就比較簡單了:
class MyActivity : BaseActivity<ActivityMyBinding>() {
override fun init() {
binding.title.text ="Hello"
binding.subtext.text ="World"
binding.button.setOnClickListener {
/* ... */
}
/* ... */
}
}
BaseFragment的封裝和使用:
abstract class BaseFragment<VB : ViewBinding> : Fragment() {
private var _binding : VB? = null
val binding :VB get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
//利用反射,調(diào)用指定ViewBinding中的inflate方法填充視圖
val type = javaClass.genericSuperclass
val clazz = (type as ParameterizedType).actualTypeArguments[0] as Class<VB>
val method = clazz.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)
_binding = method.invoke(null, layoutInflater, container, false) as VB
return _binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
init()
}
abstract fun init()
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
}
接著在我們的Fragment使用吧玻侥!
class MyFragment : BaseFragment<FragmentMyBinding>() {
override fun init() {
binding.title.text ="Hello"
binding.subtext.text ="World"
binding.button.setOnClickListener {
/* ... */
}
/* ... */
}
}
哈哈决摧,世界清凈了!4绽肌掌桩!
本文僅作為基類封裝的參考,未考慮其它因素姑食,歡迎各位給出己見波岛,共同進(jìn)步。
寫作不易音半,如對兄弟們有幫助则拷,還望點個贊。