Mavericks 项目中的 View Binding 实践:用一行 `by viewBinding()` 替代 findViewById
移动开发原生移动【免费下载链接】mavericksMavericks: Android on Autopilot项目地址https://gitcode.com/gh_mirrors/ma/mavericks点击查看免费下载View Binding 是 Google 官方推出的替代findViewById()、Kotlin 合成访问器synthetic accessors与 Butterknife 的视图绑定方案。在 Mavericks 开源仓库中官方以独立工具模块utils-view-binding的形式提供了FragmentViewBindingDelegate.kt与ViewViewBindingDelegate.kt两个委托实现让 Fragment 和自定义 View 都能用一行by viewBinding()完成绑定其语法与 Mavericks 的by fragmentViewModel()委托如出一辙。读完本文你将掌握如何在 Mavericks 架构下以最小代码量接入 View Binding并理解该委托在 Fragment 生命周期与主线程安全方面的底层设计。View Binding 是什么View Binding 是 Google 对传统视图查找方式的现代化替代它解决三类历史痛点findViewById()需要手写类型转换与空判断样板代码多且容易出错Kotlin 合成访问器synthetic accessors依赖kotlin-android-extensions插件且对命名空间与布局的引用存在隐患Butterknife通过注解与代码生成工作但引入额外的依赖与编译步骤。启用 View Binding 后编译器会为每个布局文件生成一个对应的绑定类例如counter_fragment.xml生成CounterFragmentBinding其中包含对布局内所有带id控件的强类型引用并且绑定类的根视图可以通过.root获取。虽然 View Binding 本身并非 Mavericks 专属功能但正如仓库中的 view-binding.md 文档所述Mavericks 的示例应用大量使用了自定义委托从而用一行代码完成 View Binding 的创建与生命周期管理。这种写法与 Mavericks 的 ViewModel 委托语法极其相似二者配合使用非常自然。核心用法Fragment 中一行完成绑定以仓库文档中的经典示例为骨架一个使用 View Binding 的 Mavericks Fragment 长这样class CounterFragment : BaseFragment(R.layout.counter_fragment) { private val binding: CounterFragmentBinding by viewBinding() private val viewModel: CounterViewModel by fragmentViewModel() override fun invalidate() withState(viewModel) { state - binding.textView.text Count: ${state.count} } }这里有两个值得注意的委托by viewBinding()创建并缓存CounterFragmentBinding实例之后的任意时刻直接通过binding.textView访问控件by fragmentViewModel()Mavericks 的 ViewModel 委托。两条语句风格完全一致因此在阅读代码时心智负担极小。invalidate()是 Mavericks 的状态刷新回调配合withState(viewModel)读取当前状态并更新binding.textView.text即完成了Mavrick 状态驱动 ViewBinding 视图更新的完整闭环。在仓库中这一模式被大量真实落地。例如 sample-counter 的 CounterFragment 使用by activityViewModel()共享 Activity 级 ViewModel并在invalidate()中把计数写入binding.counterTextclass CounterFragment : Fragment(R.layout.counter_fragment), MavericksView { private val binding: CounterFragmentBinding by viewBinding() private val viewModel: CounterViewModel by activityViewModel() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding.counterText.setOnClickListener { viewModel.incrementCount() } } override fun invalidate() withState(viewModel) { state - binding.counterText.text ${state.count} } }同样的写法也出现在 sample 模块的 HelloWorldFragment、DadJokeIndexFragment 等十余个 Fragment 中可见这是 Mavericks 示例应用统一采用的视图层标准写法。获取委托两种接入方式方式一直接复制委托文件按照文档说明你可以把FragmentViewBindingDelegate.kt复制进自己的工程即可使用无需引入额外依赖。注意该文件同时包含委托类与扩展函数复制时需一并保留。方式二使用仓库的 utils-view-binding 模块当前仓库已经把该能力整理成了独立 Gradle 模块utils-view-binding并在 settings.gradle 中通过include :utils-view-binding注册。该模块包含两个核心文件FragmentViewBindingDelegate.kt面向 Fragment 的委托实现与Fragment.viewBinding()扩展ViewViewBindingDelegate.kt面向自定义 ViewViewGroup的委托实现与ViewGroup.viewBinding()扩展。源码剖析FragmentViewBindingDelegate 是如何工作的把文件复制进工程后理解其内部实现有助于你正确使用它。下面按 FragmentViewBindingDelegate.kt 的源码逐段拆解。1. 通过反射获取生成的bind方法class FragmentViewBindingDelegateT : ViewBinding( bindingClass: ClassT, private val fragment: Fragment ) : ReadOnlyPropertyFragment, T { ... private val bindMethod bindingClass.getMethod(bind, View::class.java)构造时传入绑定类T::class.java并通过反射查找生成的静态方法bind(View)。这是因为框架代码不知道具体布局生成的绑定类名只能依赖约定View Binding 为每个布局生成的类都带有inflate与bind静态方法。这决定了该委托要求开启 View Binding 并持有生成的绑定类如CounterFragmentBinding。2. 生命周期感知在onDestroyView后一帧再清理引用这是该委托最精妙的部分init { fragment.lifecycleScope.launch { fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner - viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { override fun onDestroy(owner: LifecycleOwner) { // Lifecycle listeners are called before onDestroyView in a Fragment. // However, we want views to be able to use bindings in onDestroyView // to do cleanup so we clear the reference one frame later. clearBindingHandler.post { binding null } } }) } } }其设计考量可以从源码注释中读出生命周期观察器的回调发生在 Fragment 的onDestroyView()之前如果此时立即清空绑定开发者在onDestroyView()里想用 binding 做清理操作就会拿到空引用因此这里借助主线程的Handlerprivate val clearBindingHandler by lazy(LazyThreadSafetyMode.NONE) { Handler(Looper.getMainLooper()) }见源码第 20 行延迟一帧再清空引用既保证onDestroyView()阶段 binding 仍可用又确保视图销毁后不会长期持有已销毁的视图导致内存泄漏。3. getValue缓存、校验与重建override fun getValue(thisRef: Fragment, property: KProperty*): T { // onCreateView may be called between onDestroyView and next Main thread cycle. // In this case [binding] refers to the previous fragment view. Check that bindings root view matches current fragment view if (binding ! null binding?.root ! thisRef.view) { binding null } binding?.let { return it } val lifecycle fragment.viewLifecycleOwner.lifecycle if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) { error(Cannot access view bindings. View lifecycle is ${lifecycle.currentState}!) } Suppress(UNCHECKED_CAST) binding bindMethod.invoke(null, thisRef.requireView()) as T return binding!! }访问流程包含三道防线根视图一致性校验Fragment 的视图销毁与重建之间存在时间窗口缓存可能指向上一轮的旧视图。通过binding?.root ! thisRef.view判断当前 Fragment 的视图是否仍是缓存绑定的根视图不一致则丢弃缓存生命周期状态校验只有当viewLifecycleOwner.lifecycle.currentState至少达到INITIALIZED才允许访问否则直接抛出带当前状态的错误信息Cannot access view bindings. View lifecycle is ...帮助开发者尽早暴露在视图不可用时访问绑定的编码错误反射创建绑定调用bindMethod.invoke(null, thisRef.requireView())基于当前视图创建绑定实例并缓存。扩展函数本身非常简单inline fun reified T : ViewBinding Fragment.viewBinding() FragmentViewBindingDelegate(T::class.java, this)借助reified内联泛型你无需手动传入绑定类的ClassFragmentViewBindingDelegate(T::class.java, this)由编译器自动推导。自定义 View 的绑定ViewGroup.viewBinding()对于自定义 View 场景仓库提供了对称的 ViewViewBindingDelegate.ktinline fun reified T : ViewBinding ViewGroup.viewBinding() ViewBindingDelegate(T::class.java, this) class ViewBindingDelegateT : ViewBinding( private val bindingClass: ClassT, val fragment: ViewGroup ) : ReadOnlyPropertyViewGroup, T { private var binding: T? null override fun getValue(thisRef: ViewGroup, property: KProperty*): T { binding?.let { return it } val inflateMethod bindingClass.getMethod(inflate, LayoutInflater::class.java, ViewGroup::class.java) Suppress(UNCHECKED_CAST) binding inflateMethod.invoke(null, LayoutInflater.from(thisRef.context), thisRef) as T return binding!! } }与 Fragment 版不同的是它通过反射调用生成的inflate(LayoutInflater, ViewGroup)静态方法把绑定布局直接填充进自定义 View 自身因此适用于继承ViewGroup如LinearLayout的自定义控件。由于自定义 View 通常随宿主一起创建销毁这里不涉及 Fragment 生命周期实现更轻量。sample 模块的 Marquee 是真实用例它继承LinearLayout通过private val binding: MarqueeBinding by viewBinding()拿到布局绑定然后在setTitle/setSubtitle中操作binding.title、binding.subtitleModelView(autoLayout ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT) class Marquee ... : LinearLayout(context, attrs, defStyleAttr) { private val binding: MarqueeBinding by viewBinding() ... TextProp fun setTitle(title: CharSequence) { binding.title.text title } }同模块的 BasicRow.kt 也采用相同写法可作为自定义 View 绑定的参考范本。与传统 View Binding 写法的对比不用委托时标准的 Fragment View Binding 写法要求你在onCreateView中inflate绑定、在onDestroyView中手动binding null每一步都需要样板代码。而使用by viewBinding()后关注点传统写法委托写法创建绑定onCreateView中inflate并赋值首次访问时自动反射创建视图重建需手动处理旧绑定失效getValue自动比对根视图并重建释放引用onDestroyView中手动binding null生命周期观察器延迟一帧自动清理非法访问运行时表现为 NPE主动抛出带生命周期状态的错误信息在 Mavericks 场景下由于invalidate()可能在多次状态刷新中被反复调用绑定实例的稳定缓存与生命周期安全尤为重要——委托恰好把这两点都封装好了。使用前提与注意事项必须开启 View Binding委托依赖编译器生成的绑定类*Binding及其bind/inflate静态方法请确保模块的buildFeatures.viewBinding true绑定类需与布局对应private val binding: CounterFragmentBinding by viewBinding()中的类型必须与 Fragment 的布局文件匹配类型不匹配会在编译期报错访问时机委托会在 View 生命周期未达到INITIALIZED时抛出异常因此不要在onCreateView之前访问binding也不要在视图已销毁后访问线程要求绑定的创建与清理都依赖主线程Looper.getMainLooper()请保持视图相关操作在主线程进行文档原始示例中的 BaseFragment文档示例使用的BaseFragment是 Mavericks 示例中的基类继承MockableMavericksView见 sample 的 BaseFragment.kt它接收布局资源 ID 并通过Fragment(layoutRes)构造器加载布局而 sample-counter 的 CounterFragment 则直接实现了MavericksView接口。两种写法均与by viewBinding()兼容。小结View Binding 解决了 Android 视图访问的样板代码与类型安全问题而 Mavericks 仓库提供的viewBinding()委托把这一体验推向了极致——Fragment 中一行private val binding: XxxBinding by viewBinding()配合by fragmentViewModel()与invalidate()即可组成Mavericks 状态驱动 类型安全视图绑定的完整开发范式。如果你希望复用这套能力可以直接把 FragmentViewBindingDelegate.kt 与 ViewViewBindingDelegate.kt 复制进自己的工程或参照 settings.gradle 以utils-view-binding模块方式引入再对照 sample-counter 与 sample 中的真实用例开始改造你的视图层代码。赞分享移动开发原生移动【免费下载链接】mavericksMavericks: Android on Autopilot项目地址https://gitcode.com/gh_mirrors/ma/mavericks点击查看免费下载相关推荐selenium-stealth高级技巧提升reCAPTCHA v3评分的5个实用方法selenium stealth高级技巧提升reCAPTCHA v3评分的5个实用方法 在当今网络环境中reCAPTCHA v3已经成为网站检测自动化脚本的如何快速集成Mavericks与View Binding简化Android UI开发的完整指南如何快速集成Mavericks与View Binding简化Android UI开发的完整指南 MavericksAndroid on Autopilot移动开发原生移动WinUtil终极指南一键批量安装软件与Windows系统优化神器WinUtil终极指南一键批量安装软件与Windows系统优化神器 你是否厌倦了每次重装Windows系统后繁琐的软件安装过程是否被复杂的系统优化设置搞得头桌面应用运维上一篇Codeg MCP与Skills完全指南本地服务器扫描、注册中心安装与全局/项目级技能管理下一篇Ragent 多路检索完整指南向量、关键词、知识图谱、联网搜索并行召回与 RRF 融合创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

海外SaaS应用加速部署(进阶篇):从入门到实战完整指南

海外SaaS应用加速部署(进阶篇):从入门到实战完整指南

本文深入探讨海外SaaS应用加速部署(进阶篇),涵盖背景分析、原理剖析、实战步骤、配置示例、优化建议和避坑指南。很多团队在海外电商与出国场景中都会遇到与海外SaaS应用加速部署(进阶篇)相关的挑战。本文结合生产环境…

2026/10/10 2:45:04 阅读更多 →
2026年育婴师培训价格标准明细与行业现状分析

2026年育婴师培训价格标准明细与行业现状分析

2026年育婴师培训价格标准明细与行业现状分析,是很多想入行家政的姐妹最近搜索频率最高的一组词。有人在查育婴师培训费用报价到底包含哪些项目,有人在对比育婴师培训收费计划是否合理,还有人在纠结:花几千块学一门育婴技能&#…

2026/10/10 2:45:04 阅读更多 →
HarmonyOS 动画性能:一个简单出现动画为什么会掉帧?transition 和 animateTo 的使用边界【鸿蒙心迹】

HarmonyOS 动画性能:一个简单出现动画为什么会掉帧?transition 和 animateTo 的使用边界【鸿蒙心迹】

大家好,我是[晚风依旧似温柔],新人一枚,欢迎大家关注~ 本文目录:前言一、先做一个频繁显示/隐藏的组件二、问题不是 animateTo 慢,而是动画类型选错了三、为什么状态变量更新的位置会影响动画性能四、用 Profiler 不要…

2026/10/10 2:45:04 阅读更多 →

最新新闻

SpringBoot3多数据源实战:从选型配置到避坑指南

SpringBoot3多数据源实战:从选型配置到避坑指南

做后端这些年,只要业务稍微复杂一点,“一个应用连一个库”的理想状态基本撑不住。用户数据放用户库、订单数据放订单库、日志又要独立一套,再加上读写分离和多租户隔离的需求,所有问题都指向同一个核心:一个SpringBoot…

2026/10/10 3:20:15 阅读更多 →
07_在k8s集群中安装ingress-nginx

07_在k8s集群中安装ingress-nginx

文章目录0 背景与架构0.1 为什么需要ingress-nginx0.2 环境架构0.3 镜像拉取方案1 安装前准备1.1 下载ingress-nginx部署文件1.2 替换镜像地址1.3 修复DNS解析问题1.3.1 问题现象1.3.2 修复方法2 安装ingress-nginx2.1 应用部署文件2.2 验证部署3 测试Ingress3.1 部署测试应用3…

2026/10/10 3:20:15 阅读更多 →
基于Spring Boot的宠物用品商城系统开发与实践指南

基于Spring Boot的宠物用品商城系统开发与实践指南

毕业设计季总有人拿着“宠物用品系统”这个题目来找我聊,说白了这个题在Java Web方向的选题清单里几乎年年出现。还有一类人不是学生,是宠物店老板想把自己那套手工记账和微信群接单的流程搬到线上。这两类人虽然出发点不一样,但最终要的东西…

2026/10/10 3:20:15 阅读更多 →
河北省口碑好的母婴护理服务商盘点,资质齐全的正规家政机构不踩坑

河北省口碑好的母婴护理服务商盘点,资质齐全的正规家政机构不踩坑

在衡水,找月嫂、请育婴师,几乎是每个新手家庭的必经之路。可真到了挑选的时候,很多父母才发现,家政市场鱼龙混杂,有的机构只管牵线不管服务,有的月嫂短期培训就匆匆上岗,出了问题连人都找不到。…

2026/10/10 3:20:15 阅读更多 →
个人微信API二次开发:号掉线别急着新建,先救映射表

个人微信API二次开发:号掉线别急着新建,先救映射表

线上最危险的操作不是掉线本身,而是值班同学随手「再建一个号顶上」。半小时后你会看到:一半消息失败、一半回错人、客服觉得有两个机器人在抢答。 多设备与恢复路径见 GeWe 开放文档。 真正炸掉的是什么 不是微信,是你的 客户 → 主责设备…

2026/10/10 3:20:15 阅读更多 →
Docker安装报错全解析:从daemon权限到内核模块的排查指南

Docker安装报错全解析:从daemon权限到内核模块的排查指南

你有没有遇到过这样的场景:费了好大劲把 Docker 装上,兴冲冲地敲下docker ps,结果屏幕上一行红字:permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock这种感觉就像门锁装好了…

2026/10/10 3:19:15 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →