ViewBinding使用入门
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中只用
- 调用生成的绑定类中包含的静态 inflate() 方法。此操作会创建该绑定类的实例以供 Activity 使用。
- 通过调用 getRoot() 方法或使用 Kotlin 属性语法获取对根视图的引用。
- 将根视图传递到 setContentView(),使其成为屏幕上的活动视图。
private lateinit var binding: ResultProfileBindingoverride fun onCreate(savedInstanceState: Bundle) {super.onCreate(savedInstanceState)binding = ResultProfileBinding.inflate(layoutInflater)val view = binding.rootsetContentView(view)}
然后就可以使用对应的View了
binding.name.text = viewModel.namebinding.button.setOnClickListener { viewModel.userClicked() }
2.2 在Fragment中使用
- 调用生成的绑定类中包含的静态 inflate() 方法。此操作会创建该绑定类的实例以供 Fragment 使用。
- 通过调用 getRoot() 方法或使用 Kotlin 属性语法获取对根视图的引用。
- 从 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.rootreturn 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 = valuenotifyDataSetChanged()}inner class ViewHolder(binding: ItemDataBinding) : RecyclerView.ViewHolder(binding.root) {val dataIv = binding.dataIvval 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" ><Buttonandroid:id="@+id/back"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:text="Back" /><TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="Title"android:textSize="20sp" /><Buttonandroid: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"><includeandroid:id="@+id/titleBar"layout="@layout/titlebar" />
</LinearLayout>
Activity中使用
class IncludeActivity : AppCompatActivity() {private lateinit var binding: ActivityIncludeBindingoverride 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的代码:
- 删除include标签上的id属性
- 在kotlin代码中, 自己赋值ViewBinding对象:
class MergeActivity : AppCompatActivity() {private lateinit var binding: ActivityMergeBindingprivate lateinit var titlebarBinding: TitlebarMergeBindingoverride 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: VBprivate setoverride 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 ?: returnval vbClazz = type.actualTypeArguments.find {// 找到泛型声明为 实现了 ViewBinding接口的类型(it as Class<*>).genericInterfaces[0] == ViewBinding::class.java} as? Class<*> ?: returnval 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? = nullprotected val binding: VBget() = _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 ?: returnval vbClazz = type.actualTypeArguments.find {// 找到泛型声明为 实现了 ViewBinding接口的类型(it as Class<*>).genericInterfaces[0] == ViewBinding::class.java} as? Class<*> ?: returnval 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 nullval vbClazz = type.actualTypeArguments.find {// 找到泛型声明为 实现了 ViewBinding接口的类型(it as Class<*>).genericInterfaces[0] == ViewBinding::class.java} as? Class<*> ?: return nullval 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}
}
相关文章:

ViewBinding使用入门
ViewBinding 参考资料: 新技术 ViewBinding 最佳实践 & 原理击穿 更多 ViewBinding 的封装思路 1. kotlin-android-extensions(KAE) 的问题 根据Google官方的说法, KAE存在以下问题: 污染全局命名空间不能暴露可空性信息仅支持Kotlin代码 kotlin在1.4.20中 开始废弃这…...

Retrofit源码分析实践(七)【Retrofit ConvertFactory的功能实现】
Retrofit源码分析&实践系列文章目录 Retrofit源码分析&实践(一)【从使用入手分析源码】Retrofit源码分析&实践(二)【Retrofit 免费的api测试工具引入】Retrofit源码分析&实践(三)【Retrofit 代码框架搭建】Retrofit源码分析&实践(四)【Retrofit 实…...

介电常数常用测量方法综述
张扬1,徐尚志1,赵文晖2,龚增2,赵晓群1 1同济大学,上海 2上海市计量测试技术研究院,上海 在设计电路、天线、电容器等过程中经常会涉及所用材料的介电常数, 所以深入了解介电常数的相关概念对实际工作有重…...

重生之我是赏金猎人(三)-SRC漏洞挖掘-强行多次FUZZ发现某厂商SSRF到redis密码喷洒批量反弹Shell
0x00 前言 https://github.com/J0o1ey/BountyHunterInChina 欢迎大佬们点个star 最近BugBounty挖了不少,但大多数都是有手就行的漏洞,需要动脑子的实属罕见 而今天就遇到了一个非常好的案例,故作此文 0x01 对目录批量FUZZ,发…...

学会分享,学会生活,分享5款简单易用的软件。
分享是一种博爱的心境,学会分享,就学会了生活。 1.在线图片编辑工具——佐糖 佐糖是一款在线免费图片编辑工具,采用AI人工智能技术,自动识别图片,支持一键抠图,更换背景,移除水印等。另外还提…...

Redux 与 Vuex 的区别总结
Redux 工作流程 首先,用户(通过 view)发送 Action,发送方式就用到了 dispatch 方法。然后,Store 自动调用 Reducer,并传入俩个参数,当前 State 和 收到的 Action。而 Reducer 会返回新的 State。最后,Store 根据接收到的 State,判断是否发生变化。一旦发生变化,Store…...

QT(15)- QFile
1 函数 1.1 copy bool QFile::copy(const QString &newName) QFile::copy(const QString &newName) 是 Qt 中用于复制文件的函数,将 fileName() 所指向的文件复制到 newName 指定的新位置。 在复制文件之前,源文件会先被关闭。如果被复制的文…...

GaiaX开源解读 | 表达式作为逻辑动态化的基础,我们是如何设计的
GaiaX跨端模板引擎,是在阿里优酷、淘票票、大麦内广泛使用的Native动态化方案,其核心优势是性能、稳定和易用。本系列文章《GaiaX开源解读》,带大家看看过去三年GaiaX的发展过程。 前言 GaiaX【https://github.com/alibaba/GaiaX】是由优酷应…...

python中adb shell相关操作
1、python 实现adb交互,进入adb shell后,如何再发送消息 os.popen("adb -s 192.168.1.100:5555 shell \"dmesg > /data/dmesg.log\"")进入进入adb shell,在执行 dmesg > /data/dmesg.log 2、python(或BAT脚本)自…...

python 之 资源使用与控制 resource模块
一、背景 2021年做可信计算时,由于甲方给的CPU利用率不能不能超过20%; 目的:我们的程序部署甲方服务器上不能阻碍其甲方服务的正常运行 我们的程序在CPU超过20%时,可以休眠几秒后继续运行 此时需要检测控制服务器的CPU资源使用信…...

蓝库云|8项关键让你看透企业「数字转型」,零代码是惊喜
各行各业都要面对的多方竞争力及不断上涨的经营成本,以及随着时代的发展,有不少企业纷纷推动数字化转型,考虑藉着应用现代化的数据和科技工具的结合,协助企业创造新的营运模式及收入来源,以提升自动化效率、优化客户体…...

(五)、编辑页面-发布长文-富文本编辑【uniapp+uinicloud多用户社区博客实战项目(完整开发文档-从零到完整项目)】
1,edit页面 1.1 新建edit页面 1.2 从本地相册选择图片或使用相机拍照。 uni.chooseImage(OBJECT) 1.3 直接上传文件到云存储。 uploadFile(Object object) 1.4 从富文本编辑器获取编辑器内容 editorContext.getContents(OBJECT) 首页富文本编辑器初始化完成时…...

你是真的“C”——【经典面试知识点】数据在内存中的大小端存储方式
你是真的“C”——【经典面试知识点】数据在内存中的大小端存储方式😎前言🙌大小端介绍🙌什么大端小端呢?:大小端存储的标准定义:大端和小端存在的意义经典的面试题目🙌总结撒花💞&a…...

从零开始的数模(二十六)单因素方差分析
目录 一、概念 1.1相关概念 1.2用途 1.3数据要求:独立性/正态性/方差齐性 1.4步骤 编辑1.5专业名词 二、基于python的单因素方差分析 2.2单因素方差分析的作用 一、概念 1.1相关概念 单因素方差分析是一种常用的统计分析方法,它用于比较一个因…...

C++变量类型
目录 一、c中的变量定义 二、c中的变量声明 三、c中的左值和右值 一、c中的变量定义 变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。 变量定义指定一个数据类型,并包含了该类型的一个或多个变量的列表,如下所示&…...

win10 安装 vs2015(社区版本)以及opencv-4.5.5
一、下载vs2015以及opencv-4.5.5从https://msdn.itellyou.cn/ 网站下载vs2015(社区版本)从https://opencv.org/releases/网站下载opencv-4.5.5二、安装vs2015和opencv-4.5.5解压后双击exe安装文件,完成安装(默认)双击下…...

867. 转置矩阵
给你一个二维整数数组 matrix, 返回 matrix 的 转置矩阵 。矩阵的 转置 是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引。示例 1:输入:matrix [[1,2,3],[4,5,6],[7,8,9]]输出:[[1,4,7],[2,5,8],[3,6,9]]示例 2&a…...

Datawahle组队学习——妙趣横生大数据 Day1
妙趣横生大数据 Day1[妙趣横生大数据 Juicy Big Data](https://datawhalechina.github.io/juicy-bigdata/#/?id妙趣横生大数据-juicy-big-data)一、大数据概述大数据——第三次信息化浪潮大数据概念大数据应用大数据关键技术二、Hadoop背景介绍特性项目架构实验1. 准备工作2. …...

网友眼中越老越吃香的行业,果然是风向变了!
越老越吃香的行业,一直都是被热议的话题。对于年轻人来说,找到一个适合自己的并且具有前景的工作,不是一件容易的事情。 最近,看到有人在平台上问相关的问题,本着认真看一看的态度点进去,却差点被热评第一…...

为什么时间序列预测这么难?本文将给你答案
机器学习和深度学习已越来越多应用在时序预测中。ARIMA 或指数平滑等经典预测方法正在被 XGBoost、高斯过程或深度学习等机器学习回归算法所取代。 尽管时序模型越来越复杂,但人们对时序模型的性能表示怀疑。有研究表明,复杂的时序模型并不一定会比时序…...

STC15系列单片机通过串口多字节数据读写EEPROM操作
STC15系列单片机通过串口多字节数据读写EEPROM操作📌相关篇《STC15系列单片机EEPROM读写示例》 ⛳手册勘误信息注意事项 ⚡在手册上面描述STC15F2K60S2及STC15L2K60S2系列单片机内部EEPROM还可以用MOVC指令读,但此时首地址不再是0000H,而是程…...

计算机网络-ip数据报
在图中,网络层包含了四种协议:ARP、IP、ICMP、IGMP,由上下关系表明,ARP为IP协议服务,IP为ICMP和IGMP服务。 IP数据报格式 此处不区分数据报和分组的概念:当数据部分过长时,将数据部分拆分&…...

从零开始学C
以下是 该如何学习C语言的【思维导图】以及部分重点知识点的【博客链接】。其实C语言并不难,难的是没有人去教,没有耐心去学。不知道从哪下手学习,我将C的知识点做成一个思维导图,以供迷茫的小白参考,哪里不会…...

【云原生】手把手带你从零开始搭建kubernetes最新版本实战
文章目录前言一. 实验环境二. k8s 的介绍三 . k8s的安装3.1 搭建实验环境3.1.1 硬件层面的要求3.1.2 软件层面环境配置3.2 docker的安装3.2.1 搭建docker3.2.2 部署 cri-dockerd3.3 部署k8s3.3.1 配置添加阿里云的yum源3.3.2 安装kubeadm kubelet kubectl3.3.3 k8s-master节点初…...

trivy os软件包扫描原理分析
具体可以基于之前的博客来做 基于trivy获取基础镜像 参数修改一下: cliOpt.ListAllPkgs true 结果中会带有如下格式的结果: "Results":[{"Target":"192.168.1.94:443/test22/centos:7 (centos 7.9.2009)","Clas…...

算法训练营 day48 动态规划 完全背包 零钱兑换 II 组合总和 Ⅳ
算法训练营 day48 动态规划 完全背包 零钱兑换 II 组合总和 Ⅳ 完全背包 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物…...

Java 基础(1)—泛型简单使用
一、泛型定义及作用 泛型是一种编程机制,允许在编写代码时使用参数化类型,以在编译时实现类型安全。 以下是泛型作用: 增强代码可读性和可维护性:通过在代码中使用泛型参数,可以使代码更清晰、更具有可读性和可维护性…...

内存卡损坏了怎么恢复?
内存卡损坏了怎么恢复?现在我们身边有不少电子设备都是用存储卡来存储数据的。一旦需要方便我们直接导出使用。但这存储的数据也不是一定安全的,当我们将内存卡连接到电脑时,难免会碰到病毒、格式化等提示,在这些情况下,可能会导…...

Mysql使用规范(纯技术和实战建议)
1、事务隔级别: (强制):Repeatable-Read(重复读),且不能在会话操作时临时开启隔离级别。 注: Repeatable-Read(重复读)隔离级别解决不了幻读。 可用 show variables l…...

Netty源码解读-EventLoop(二)
一、简介 NioEventLoop的重要组成:Selector、线程、任务队列,他既会处理io事件,也会处理普通任务和定时任务. 1.下面是Selector,注意有两个哦后面会讲 2.下面的爷爷类提供的Thread变量,其实下面发excutor用的就是这个…...