Android Studio 3.6中有一项名为“视图绑定”的新功能。就像数据绑定一样,只不过它要做的就是采用您现有的布局,并为其生成绑定-无需更改,无需<layout标签,尤其是无需<data标签。只是纯布局!
启用ViewBinding
首先,我们在build.gradle中启用它:
viewBinding {
enabled = true
}
Activity绑定视图
普通xml文件 activity_first.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Activity
class FirstActivity: AppCompatActivity() {
/**
* ActivityFirstBinding由Android Studio自动生成(需要升级到3.6.0)所以基本不需要考虑编译性能(这也是和dataBinding差别最大的地方)
*/
private lateinit var binding: ActivityFirstBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFirstBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.textView.text = "已经绑定视图了"
}
}
Fragment绑定视图
fragment_bind.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".InflateFragment">
<TextView
android:id="@+id/textViewFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>
Fragment
/**
* View Binding example with a fragment that uses the alternate constructor for inflation and
* [onViewCreated] for binding.
*/
class BindFragment : Fragment(R.layout.fragment_blank) {
// Scoped to the lifecycle of the fragment's view (between onCreateView and onDestroyView)
private var fragmentBlankBinding: FragmentBlankBinding? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentBlankBinding.bind(view)
fragmentBlankBinding = binding
binding.textViewFragment.text = getString(string.hello_from_vb_bindfragment)
}
override fun onDestroyView() {
// Consider not storing the binding instance in a field, if not needed.
fragmentBlankBinding = null
super.onDestroyView()
}
}
Activity写法还算比较简单,但是Fragment稍微比较麻烦,但我们仍然有方法改进它。通过改进,我们能够实现:一行代码完成绑定
原理: kotlin自定义委托
我们需要做的就是将MyBinding.bind函数引用传递给我们的委托,并使用该函数初始化View的绑定。然后,当视图被销毁时,我们清除此绑定值。
委托类FragmentViewBindingDelegate
class FragmentViewBindingDelegate<T : ViewBinding>(
val fragment: Fragment,
val viewBindingFactory: (View) -> T
) : ReadOnlyProperty<Fragment, T> {
private var binding: T? = null
init {
fragment.lifecycle.addObserver(object: DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner ->
viewLifecycleOwner?.lifecycle?.addObserver(object: DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
binding = null
}
})
}
}
})
}
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
val binding = binding
if (binding != null) {
return binding
}
val lifecycle = fragment.viewLifecycleOwner.lifecycle
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.")
}
return viewBindingFactory(thisRef.requireView()).also { this@FragmentViewBindingDelegate.binding = it }
}
}
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
FragmentViewBindingDelegate(this, viewBindingFactory)
委托后的Fragment绑定
class BindFragment : Fragment(R.layout.fragment_blank) {
/**
* 通过委托调用绑定方法
*/
private val fragmentBlankBinding: FragmentBlankBinding by viewBinding(FragmentBlankBinding::bind)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
fragmentBlankBinding.textViewFragment.text = "通过委托绑定成功"
}
}
定义Activity绑定委托
inline fun <T : ViewBinding> AppCompatActivity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T)
= lazy(LazyThreadSafetyMode.NONE) {
bindingInflater.invoke(layoutInflater)
}
委托后的Activity绑定
class FirstActivity: AppCompatActivity() {
/**
* ActivityFirstBinding由Android Studio自动生成(需要升级到3.6.0)所以基本不需要考虑编译性能(这也是和dataBinding差别最大的地方)
*/
private val binding by viewBinding(ActivityFirstBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.textView.text = "已经通过委托绑定视图了"
}
}
借助Kotlin代表和Jetpack Lifecycle组件的强大功能,我们已将ViewBinding变成了单行调用