比`kotlin-android-extensions`插件更好用的`ViewBinding`

kotlin-android-extensions插件更好用的 ViewBinding


前言

因爲今年工做比較忙,很久沒更新文章了,幾年作的項目比較多,最近會陸陸續續記錄一下今年遇到的問題或知識。android

1、kotlin-android-extensions存在的問題

一、污染全局命名空間安全

二、不能暴露可空性信息app

三、僅支持 Kotlin 代碼ide

這裏有篇文章有說明,具體能夠參考這裏:https://www.bennyhuo.com/2020/11/07/deprecated-kotlin-android-extensions/佈局

注意:根據谷歌的將來計劃,在接下來的一年裏,谷歌的團隊將共同棄用 synthetics,並繼續支持——「視圖綁定 (View Binding)」post

具體說明能夠看這裏:https://mp.weixin.qq.com/s/pa1YOFA1snTMYhrjnWqIgggradle

2、ViewBinding的使用

一、設置說明

viewBinding 元素添加到其 build.gradle 文件中,以下例所示:ui

android { 
        ...
        viewBinding { 
            enabled = true
        }
    }

若是要忽略某個佈局文件,請將 tools:viewBindingIgnore="true" 屬性添加到相應佈局文件的根視圖中:spa

<LinearLayout ... tools:viewBindingIgnore="true" >
       ...
</LinearLayout>

二、用法

啓用視圖綁定功能後,系統會爲該模塊中包含的每一個 XML 佈局文件生成一個綁定類。每一個綁定類均包含對根視圖以及具備 ID 的全部視圖的引用。系統會經過如下方式生成綁定類的名稱:將 XML 文件的名稱轉換爲駝峯式大小寫,並在末尾添加「Binding」一詞插件

例如,假設某個佈局文件的名稱爲 result_profile.xml

<LinearLayout ... >
        <TextView android:id="@+id/name" />
        <ImageView android:cropToPadding="true" />
        <Button android:id="@+id/button" android:background="@drawable/rounded_button" />
</LinearLayout>

所生成的綁定類的名稱就爲 ResultProfileBinding。此類具備兩個字段:一個是名爲 nameTextView,另外一個是名爲 buttonButton。該佈局中的 ImageView 沒有 ID,所以綁定類中不存在對它的引用。

每一個綁定類還包含一個 getRoot() 方法,用於爲相應佈局文件的根視圖提供直接引用。在此示例中,ResultProfileBinding 類中的 getRoot() 方法會返回 LinearLayout 根視圖。

如下幾個部分介紹了生成的綁定類在 Activity 和 Fragment 中的使用。

在 Activity 中使用視圖綁定

如需設置綁定類的實例以供 Activity 使用,請在 Activity 的 onCreate()方法中執行如下步驟:

  1. 調用生成的綁定類中包含的靜態 inflate() 方法。此操做會建立該綁定類的實例以供 Activity 使用。
  2. 經過調用 getRoot() 方法或使用 Kotlin 屬性語法獲取對根視圖的引用。
  3. 將根視圖傳遞到setContentView(),使其成爲屏幕上的活動視圖。
private lateinit var binding: ResultProfileBinding

    override fun onCreate(savedInstanceState: Bundle) { 
        super.onCreate(savedInstanceState)
        binding = ResultProfileBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)
    }

您如今便可使用該綁定類的實例來引用任何視圖:

binding.name.text = viewModel.name
    binding.button.setOnClickListener {  viewModel.userClicked() }
在 Fragment 中使用視圖綁定

如需設置綁定類的實例以供 Fragment 使用,請在 Fragment 的 onCreateView() 方法中執行如下步驟:

  1. 調用生成的綁定類中包含的靜態 inflate() 方法。此操做會建立該綁定類的實例以供 Fragment 使用。
  2. 經過調用 getRoot() 方法或使用 Kotlin 屬性語法獲取對根視圖的引用。
  3. onCreateView() 方法返回根視圖,使其成爲屏幕上的活動視圖。

注意inflate() 方法會要求您傳入LayoutInflater。若是佈局已inflate,您能夠調用綁定類的靜態 bind() 方法。

private var _binding: ResultProfileBinding? = null
    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? { 
        _binding = ResultProfileBinding.inflate(inflater, container, false)
        val view = binding.root
        return view
    }

    override fun onDestroyView() { 
        super.onDestroyView()
        _binding = null
    }

您如今便可使用該綁定類的實例來引用任何視圖:

binding.name.text = viewModel.name
    binding.button.setOnClickListener {  viewModel.userClicked() }

注意:Fragment 的存在時間比其視圖長。請務必在 Fragment 的 onDestroyView() 方法中清除對綁定類實例的全部引用。

與 findViewById 的區別
  • Null 安全
  • 類型安全
DataBinding的對比
  • 更快的編譯速度
  • 易於使用

3、ViewBinding 的封裝

一、activity中的封裝

基類

abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() { 

    private var mBinding: T? = null

    override fun onCreate(savedInstanceState: Bundle?) { 
        super.onCreate(savedInstanceState)
        setContentView(getViewBinding().also { 
            mBinding = it
        }.root)
    }

    abstract fun getViewBinding(): T

    override fun onDestroy() { 
        super.onDestroy()
        mBinding = null
    }
}

MainActivity繼承這個基類並實現getViewBinding這個方法`

override fun getViewBinding(): ActivityMainBinding { 
        return ActivityMainBinding.inflate(layoutInflater)
    }

二、fragment中的封裝

基類

abstract class BaseFragment<T : ViewBinding> : Fragment() { 

    private var mBaseBinding: T? = null

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? { 
        return onCreateBinding(inflater, container, savedInstanceState).also { 
            mBaseBinding = it
        }.root
    }

    abstract fun onCreateBinding(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): T

    override fun onDestroyView() { 
        super.onDestroyView()
        mBaseBinding = null
    }
}

FirstFragment繼承基類並實現如下:

override fun onCreateBinding(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): FragmentEarnBinding { 
        return FragmentFirstBinding.inflate(inflater, container, false)
    }

更好的封裝能夠參考這篇文章:https://juejin.cn/post/6906153878312452103

4、使用時的問題

在使用include引入佈局時

使用bind將include所包含的佈局引入進來;

layoutToolBarBinding = LayoutToolBarBinding.bind(ActivityMainBinding.inflate(layoutInflater().root());

在使用ViewStub引入佈局時

// mBinding是Activity或Fragment的viewbinding
   // customViewstub爲 Viewstub的id
   mBinding.customViewstub.setOnInflateListener {  stub, inflated ->
                run { 
                //LayoutCustomBinding 爲viewStuby所引用的佈局生成的viewbinding
                   LayoutCustomBinding.bind(inflated)  
                }
            }
   mBinding.customViewstub.inflate()


您的關注和點贊是我分享的動力,若有幫助請勿吝嗇!ヽ( ̄▽ ̄)ノ

相關文章
相關標籤/搜索