ViewBinding使用入门

2023-05-16

ViewBinding

参考资料:
新技术 ViewBinding 最佳实践 & 原理击穿
更多 ViewBinding 的封装思路

1. kotlin-android-extensions(KAE) 的问题

根据Google官方的说法, KAE存在以下问题:

  • 污染全局命名空间
  • 不能暴露可空性信息
  • 仅支持Kotlin代码

kotlin在1.4.20中 开始废弃这个库了(https://blog.jetbrains.com/kotlin/2020/11/kotlin-1-4-20-released/#Deprecation_of_Kotlin_Android_Extensions)
ReleaseNote中的原因是 KAT所填补的空白已经被ViewBinding所代替了, 所以他们不再支持了…

2. ViewBinding的使用

1. 开启ViewBinding

在model的build.gradle 中开启

android {
    viewBinding {
        enabled = true
    }
}

之后每个xml布局都会生成对应ViewBinding类

如果想要忽略摸个布局文件, 可以添加tools:viewBindingIgnore="true"属性

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

2. 基本用法

生成的ViewBinding类使用以下方式的命名规则:将 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。此类具有两个字段:一个是名为 name 的 TextView,另一个是名为 button 的 Button。该布局中的 ImageView 没有 ID,因此绑定类中不存在对它的引用。

每个绑定类还包含一个 getRoot() 方法,用于为相应布局文件的根视图提供直接引用。在此示例中,ResultProfileBinding 类中的 getRoot() 方法会返回 LinearLayout 根视图。

每一个Binding类都提供了3中创建ViewBinding对象的方式, 可以根据情况选择:

位置:

ViewBinding的用法基本上都是在围绕着上面的3个方法

2.1 在Activity中只用

  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)
    }

然后就可以使用对应的View了

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

2.2 在Fragment中使用

  1. 调用生成的绑定类中包含的静态 inflate() 方法。此操作会创建该绑定类的实例以供 Fragment 使用。
  2. 通过调用 getRoot() 方法或使用 Kotlin 属性语法获取对根视图的引用。
  3. 从 onCreateView() 方法返回根视图,使其成为屏幕上的活动视图。
    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
    }
    

2.3 在Adapter中使用ViewBinding

在Adapter中使用ViewBinding与之前没有什么不同

class SimpleDataAdapter : RecyclerView.Adapter<SimpleDataAdapter.ViewHolder>() {
    var dataList: List<Data> = emptyList()
        set(value) {
            field = value
            notifyDataSetChanged()
        }

    inner class ViewHolder(binding: ItemDataBinding) : RecyclerView.ViewHolder(binding.root) {
        val dataIv = binding.dataIv
        val dataTv = binding.dataTv
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemDataBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        dataList[position].let {
            holder.dataIv.setImageResource(it.img)
            holder.dataTv.text = it.txt
        }
    }

    override fun getItemCount(): Int = dataList.size
}

2.4 对引入布局使用ViewBinding

2.4.1 include

比如有如下布局: titlebar.xml 希望作为一个通用布局引入到其他布局中

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:text="Back" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Title"
        android:textSize="20sp" />

    <Button
        android:id="@+id/done"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:text="Done" />

</RelativeLayout>

activity的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include
        android:id="@+id/titleBar"
        layout="@layout/titlebar" />
</LinearLayout>

Activity中使用

class IncludeActivity : AppCompatActivity() {
    private lateinit var binding: ActivityIncludeBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityIncludeBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.titleBar.title.text = "Title"
        binding.titleBar.back.setOnClickListener {
        }
        binding.titleBar.done.setOnClickListener {
        }
    }

}

这里可以看到 binding中根据id生成了一个titleBar的对象, 查看ViewBinding生成的代码可知, 这个titleBar还是一个ViewBinding的类:

2.4.2 merge

如果被引入的布局根标签是merge, 则不能使用上述方式了, 疑问merge会将被引入的布局直接合并到对应的位置, 所以在ViewBinding中, 通过id找对应的ViewBinding的过程中会失败

相较于2.4.1的代码:

  1. 删除include标签上的id属性
  2. 在kotlin代码中, 自己赋值ViewBinding对象:
class MergeActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMergeBinding
    private lateinit var titlebarBinding: TitlebarMergeBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMergeBinding.inflate(layoutInflater)
        setContentView(binding.root)
        titlebarBinding = TitlebarMergeBinding.bind(binding.root)
        titlebarBinding.title.text = "Title"
        titlebarBinding.back.setOnClickListener {
        }
        titlebarBinding.done.setOnClickListener {
        }
    }

}

3. 封装

封装的主要思路是尽量不写,或少写一些模板代码, 主要的思路就是在创建ViewBinding对象上实现自动创建

3.1 Activity

open class BaseReflectionAty<VB : ViewBinding> : AppCompatActivity() {
    protected lateinit var binding: VB
        private set

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        generateViewBinding()
        if (this::binding.isInitialized) {
            setContentView(binding.root)
            binding.initViews()
        }
    }

    open fun VB.initViews() {}

    /**
     * 生成ViewBinding
     */
    private fun generateViewBinding() {
        val type = this::class.java.genericSuperclass as? ParameterizedType ?: return
        val vbClazz = type.actualTypeArguments.find {
            // 找到泛型声明为 实现了 ViewBinding接口的类型
            (it as Class<*>).genericInterfaces[0] == ViewBinding::class.java
        } as? Class<*> ?: return

        val method = vbClazz.getMethod("inflate", LayoutInflater::class.java)
        binding = method.invoke(null, layoutInflater) as VB
    }
}

在onCreate时, 通过generateViewBinding方法来生成对应的ViewBinding对象,使用时:

class MainActivity : BaseReflectionAty<ActivityMainBinding>() {
    override fun ActivityMainBinding.initViews() {
        mainTextView.setOnClickListener {
            Toast.makeText(this@MainActivity, "lalala", Toast.LENGTH_SHORT).show()
        }
    }
}

直接在initViews中使用布局文件中东的View即可

3.2 Fragment

Fragment的思路与Activity相同

class BaseReflectionFragment<VB : ViewBinding> : Fragment() {
    private var _binding: VB? = null
    protected val binding: VB
        get() = _binding!!


    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        generateViewBinding(container)

        if (_binding != null) {
            return binding.root
        }
        return super.onCreateView(inflater, container, savedInstanceState)
    }

    open fun VB.initViews() {}

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        _binding?.initViews()
    }

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

    /**
     * 生成ViewBinding
     */
    private fun generateViewBinding(rootView: ViewGroup?) {
        val type = this::class.java.genericSuperclass as? ParameterizedType ?: return
        val vbClazz = type.actualTypeArguments.find {
            // 找到泛型声明为 实现了 ViewBinding接口的类型
            (it as Class<*>).genericInterfaces[0] == ViewBinding::class.java
        } as? Class<*> ?: return

        val method = vbClazz.getMethod(
            "inflate",
            LayoutInflater::class.java,
            ViewGroup::class.java,
            Boolean::class.java
        )
        _binding = method.invoke(null, layoutInflater, rootView, false) as VB
    }
}

3.3 Adapter

ViewHolder:

class BaseViewHolder<VB : ViewBinding>(val binding: VB) : RecyclerView.ViewHolder(binding.root)

Adapter:

abstract class ViewBindingAdapter<VB : ViewBinding> : RecyclerView.Adapter<BaseViewHolder<VB>>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<VB> {
        generateViewBinding(parent)?.let {
            return BaseViewHolder(it)
        } ?: TODO("Not yet implemented")
    }

    private fun generateViewBinding(parent: ViewGroup): VB? {
        val type = this::class.java.genericSuperclass as? ParameterizedType ?: return null
        val vbClazz = type.actualTypeArguments.find {
            // 找到泛型声明为 实现了 ViewBinding接口的类型
            (it as Class<*>).genericInterfaces[0] == ViewBinding::class.java
        } as? Class<*> ?: return null
        val layoutInflater = LayoutInflater.from(parent.context)

        val method = vbClazz.getMethod(
            "inflate",
            LayoutInflater::class.java,
            ViewGroup::class.java,
            Boolean::class.java
        )
        return method.invoke(null, layoutInflater, parent, false) as VB
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ViewBinding使用入门 的相关文章

  • ViewBinding和ButterKnife

    一 ViewBinding和ButterKnife比较 xff1a 传送门 1 ButterKnife一个好处就是不用写findViewById xff0c 另一个好处就是不用写setOnClickListener之类的 xff0c 满屏幕
  • kotlin-android-extensions过时了,迁移到ViewBinding

    前言 回顾历史 xff0c kotlin android extensions插件让我们省去了很多findViewById的代码 xff0c 直接使用控件id操作控件 不过在Android Studio 4 1及以上IDE新建项目的时候 x
  • ViewBinding - Jetpack 视图绑定委托封装及使用示例

    通过视图绑定功能 xff0c 您可以更轻松地编写可与视图交互的代码 在模块中启用视图绑定之后 xff0c 系统会为该模块中的每个 XML 布局文件生成一个绑定类 绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用 在大多数情况下
  • AndroidStudio:使用ViewBinding时,找不到关联的布局文件的问题

    使用ViewBinding代替findViewById还是比较方便的 至于用法请自行百度 很多也很详细 在使用ViewBinding时本人却遇到了 点击class左边的布局关联时 却跳转不到xml布局文件 解决办法 点击系统生成的Activ
  • ViewBinding的用法

    1 启用Viewbinding功能 xff1a 在模块build gradle文件android节点下添加如下代码 android span class token punctuation span span class token pun
  • Android开发:Fragment中优雅使用ViewBinding【Java】

    目录 前言 官网示例 封装 前言 ViewBinding可以帮助我们减少代码中的大部分findViewById xff0c 官网中提到了它的优点和缺点 xff1a Null 安全 xff1a 由于视图绑定会创建对视图的直接引用 xff0c
  • Jetpack之ViewBinding

    ViewBinding 的作用 xff1a 代替findViewById xff0c 还可以保证空安全和类型安全 基本原理 会为每一个布局xml文件生成一个视图绑定类 xff0c 类名称是布局文件名转化为UpperCamelCase 43
  • Jetpack学习-6-Viewbinding使用及简单分析

    Jetpack可以帮助开发者减少样板代码 而findviewbyId正是需要减少的样板代码 于是就有了ViewBinding 一开始我细看Jetpack的各个组件我是懵的 里面没有ViewBinding 这不好把它归纳到Jetpack系列中
  • 是时候拥抱ViewBinding了~

    是时候拥抱ViewBinding了 xff01 xff01 一 前言二 初识ViewBinding三 拥抱ViewBinding3 1 环境要求3 2 开启ViewBinding功能3 3 Activity中ViewBinding的使用3
  • ViewBinding与Kotlin委托

    接上篇幅 自定义属性委托的用处很多 xff0c 例如组合替代继承 xff0c 给个ViewBinding在Fragment中的使用的例子 xff1a 委托 自定义属性委托 lt p gt lt p gt lt ul gt lt li gt
  • ViewBinding使用入门

    ViewBinding 参考资料 新技术 ViewBinding 最佳实践 amp 原理击穿 更多 ViewBinding 的封装思路 1 kotlin android extensions KAE 的问题 根据Google官方的说法 KA
  • Android viewBinding让你告别findViewById和ButterKnife

    很久没有更新博客了 xff0c 不是因为别的 xff0c 就是懒 今天要分享的一个新技术 xff0c 从此告别定义一大串的UI控件变量 xff0c 再也不用写findViewById xff0c 也不需要依赖ButterKnife和写一堆
  • android ViewBinding

    一 kotlin android extensions 在使用ViewBinding之前 xff0c 我们一直使用的是kotlin android extensions xff0c 使用kotlin android extensions可以
  • Android ViewBinding的使用详解

    一 什么是view binding 视图绑定会替代 findViewById 通过视图绑定功能 xff0c 您可以更轻松地编写可与视图交互的代码 在模块中启用视图绑定之后 xff0c 系统会为该模块中的每个 XML 布局文件生成一个绑定类
  • 【Android Jetpack系列】一、ViewBinding的使用

    关于本系列的说明 作为学习Jetpack的系列文章 可能会更新得很慢 本系列文或者应该称之为学习笔记 观看本文的同学 应该已经有具备开发简单Android App的能力了 若是零基础 那么阅读本文可能有些难懂 我只能尽量简单解释 本文所用开
  • 【Linux】Ubuntu使用入门

    前言 本文主要记录一些Ubuntu中常用的基本操作 xff0c 记录自己的实践经历 xff0c 不断更新 xff01 xff01 xff01 0 基本文件交互 在Ubuntu系统中 xff0c 右键是没有创建文件的选项的 xff0c 只能创
  • Ubuntu下cmake使用入门

    CMake是一个跨平台的安装 xff08 编译 xff09 工具 xff0c 可以用简单的语句来描述所有平台的安装 编译过程 他能够输出各种各样的makefile或者project文件 其包含自己的语法结构 xff0c 只要按照其语法编写成
  • git的使用入门

    1 添加个人信息 git config global user name 名字 git config global user email 邮箱 git config global user phone 手机号 查看是否提交 git conf
  • Android ViewBinding更新,include得到优化

    前言 之前的ViewBinding对include很不友好 以至于用起来很是难受 到目前优化后不再强制转换FrameLayout布局 使用更加舒适 解决 用之前的例子 直接上代码 这次我们给include的xml加上id
  • 【Android】ViewBinding+DataBinding+MVVM新手快速上手

    为什么写这篇博客 网上大部分博客 代码量都比较大 把实际的业务都代入进去了 这篇博客的目的 就是为了讲解基本原理和使用思路 然后给出一个最简单的Demo 这里不讲解具体用法 那样篇幅会太长 直接看Demo代码就行 什么是ViewBindin

随机推荐