通过视图绑定功能,您可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。
在大多数情况下,视图绑定会替代
findViewById
。
⚠️ Kotlin Android Extensions 已弃用,这意味着不再支持使用 Kotlin 合成进行视图绑定。 如果您的应用使用 Kotlin 合成进行视图绑定,请迁移到 Jetpack 视图绑定。
视图绑定功能可按模块启用。要在某个模块中启用视图绑定,请将 viewBinding
元素添加到其 build.gradle
文件中,如下例所示:
android {
...
viewBinding {
enabled = true
}
}
如果您希望在生成绑定类时忽略某个布局文件,请将 tools:viewBindingIgnore="true"
属性添加到相应布局文件的根视图中:
...
简单使用:可参考 ViewBinding-视图绑定用法
我有在网上找到封装好的写法,这里借鉴分享一下:委托 Android 视图绑定以及使用示例
这里我也贴一份出来:
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.viewbinding.ViewBinding
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/**
* Activity 绑定委托,可以从 onCreate 到 onDestroy(含)使用
*/
inline fun AppCompatActivity.viewBinding(crossinline factory: (LayoutInflater) -> T) =
lazy(LazyThreadSafetyMode.NONE) {
factory(layoutInflater)
}
/**
* Fragment 绑定委托,可以从 onViewCreated 到 onDestroyView(含)使用
*/
fun Fragment.viewBinding(factory: (View) -> T): ReadOnlyProperty =
object : ReadOnlyProperty, DefaultLifecycleObserver {
private var binding: T? = null
override fun getValue(thisRef: Fragment, property: KProperty<*>): T =
binding ?: factory(requireView()).also {
// 如果在 Lifecycle 被销毁后访问绑定,则创建新实例,但不要缓存它
if (viewLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
viewLifecycleOwner.lifecycle.addObserver(this)
binding = it
}
}
override fun onDestroy(owner: LifecycleOwner) {
binding = null
}
}
/**
* 实现 onCreateDialog 的 DialogFragment 的绑定委托(像活动一样,它们没有单独的视图生命周期),
* 可以从 onCreateDialog 到 onDestroy(包括)使用
*/
inline fun DialogFragment.viewBinding(crossinline factory: (LayoutInflater) -> T) =
lazy(LazyThreadSafetyMode.NONE) {
factory(layoutInflater)
}
/**
* 不是真正的委托,只是 RecyclerView.ViewHolders 的一个小帮手
*/
inline fun ViewGroup.viewBinding(factory: (LayoutInflater, ViewGroup, Boolean) -> T) =
factory(LayoutInflater.from(context), this, false)
/**
* 不是真正的委托,只是 CustomView 的一个小帮手
*/
inline fun ViewGroup.viewBinding(
factory: (LayoutInflater, ViewGroup, Boolean) -> T,
attachToRoot: Boolean = false
) = factory(LayoutInflater.from(context), this, attachToRoot)
1. Activity中使用:
class MainActivity : AppCompatActivity() {
private val binding by viewBinding(ActivityMainBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.button.text = "Bound!"
}
}
2. Fragment中使用:
// 不要忘记在 Fragment 构造函数中传递 layoutId
class RegularFragment : Fragment(R.layout.fragment) {
private val binding by viewBinding(FragmentBinding::bind)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.button.text = "Bound!"
}
}
3.DialogFragment中使用:(两种写法)
// 带有 onCreateDialog 的 DialogFragment 没有视图生命周期,所以我们需要一个不同的委托
class DialogFragment1 : DialogFragment() {
private val binding by viewBinding(FragmentBinding::inflate)
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding.button.text = "Bound!"
return AlertDialog.Builder(requireContext()).setView(binding.root).create()
}
}
// 对于具有完整视图的 DialogFragment,我们可以使用常规的 Fragment 委托(实际上是这里的整个代码与在 RegularFragment 中的完全一样)
// 注意:最近才添加了具有 layoutId 的构造函数(在 Fragment 1.3.0 中)
class DialogFragment2 : DialogFragment(R.layout.fragment) {
private val binding by viewBinding(FragmentBinding::bind)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.button.text = "Bound!"
}
}
4.DialogFragment中使用:(三种写法)
// 对于 RecyclerView,我们不需要任何委托,只需要一个属性。
// 不幸的是,这里我们有一个名称重载:View Binding vs “binding” holder to data (onBindViewHolder)。
// ViewGroup.viewBinding() 辅助函数可以稍微减少样板。
class Adapter1 : ListAdapter(Differ()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
return Holder(parent.viewBinding(ListItemBinding::inflate))
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.binding.textView.text = getItem(position)
}
class Holder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root)
private class Differ : DiffUtil.ItemCallback() { ... }
}
// 或者,我们可以为所有适配器使用通用 BoundHolder
class Adapter2 : ListAdapter>(Differ()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BoundHolder {
return BoundHolder(parent.viewBinding(ListItemBinding::inflate))
}
override fun onBindViewHolder(holder: BoundHolder, position: Int) {
holder.binding.textView.text = getItem(position)
}
private class Differ : DiffUtil.ItemCallback() { ... }
}
open class BoundHolder(val binding: T) : RecyclerView.ViewHolder(binding.root)
// 就我个人而言,我更喜欢将视图创建和操作封装在 ViewHolder 中。
// 在这种情况下,BoundHolder 可以用作超类。
class Adapter3 : ListAdapter(Differ()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = Holder(parent)
override fun onBindViewHolder(holder: Holder, position: Int) = holder.bind(getItem(position))
class Holder(parent: ViewGroup) : BoundHolder(parent.viewBinding(ListItemBinding::inflate)) {
fun bind(item: String) {
binding.textView.text = item
}
}
private class Differ : DiffUtil.ItemCallback() { ... }
}
abstract class BoundHolder(protected val binding: T) : RecyclerView.ViewHolder(binding.root)
5.自定义View中使用:
class CustomLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val binding: ViewCustomLayoutBinding by lazy {
viewBinding(
ViewCustomLayoutBinding::inflate,
true
)
}
init{
binding.textView.text = "Test"
}
}
具体的项目架构地址:MVVM-Project-Hilt
(欢迎讨论)