老夫恐怕是得了病。是一种不会拖累别人,也不太会有什么身体上感受到痛苦的病,所以也都不用担心。这只是说,是在心理上有了那么一点点向着古怪的方向赼趄的意思。
一开始的时候,是从破烂老旧的硬件开始的。这里的硬件,并不专门特指电子产品的范畴,虽然确实是这个范畴内的会多一些,但其他的——我把具备物理形体的物体——硬件也不少。最典型的就是一个白色的球状玻璃灯罩,它甚至原来不属于我,但是归属权转移到我之后,到现在快十年了,它还好端端地放在那儿,没有被破坏,没有被遗弃,也没有被保护,也没有被利用。究其原因,只是因为,它还是个物件。至于老旧手机,老旧笔记本电脑,唉,一声叹息,经过反复多次的断舍离大清除,也还是留有不少。往小黄鱼集市上挂的时候,左看看右看看,不断寻找优点、特点,残存着留下的执念,然后再把价格定的高一些,这样虽说真的脱手的时候会多收入那么一两杯瑞幸咖啡,但本质上却是在延缓它被新主人拍下的速度。
最近这股歪风持续刮到了,非硬件领域,好比说写的程序。程序可能都说大了,连代码段都不放过。就说前几天吧,一开始想要一个功能,是在手机上展示一个浮动窗口,上面有几个操作按钮,此外如果点到按钮以外的区域,就可以在屏幕上移动它,用小兄弟的话来说,就是个“遥控器”。实现的方式不止一种,各有利弊。用一个独立的 Window 来实现,却又要裁剪它的大小,貌似要跟更高的权限打交道,因为需求并没有那么重量级,所以最后还是选择使用了 activity,只不过要把它的背景搞成透明的,由此那个可以拖动的“遥控器”实际上也就不是在桌面上移动,而是在 activity 里面。不记得是跟 Windsurf 还是 Cursor 合作的,总之一个雏形状的 activity 最后还是写成了。
测试了几天,一次突然想到,透明部分虽然能让用户看到底下的内容,却无法操作,是不是还是改成 Window 比较好一些?正好有人在网上吹爆了 Grok3,于是就跟它提了一样的要求。这个过程和结果都不怎么样,总之就是 Window 方案似乎还是不可行。那退而求其次,想看看即便仍旧采用 activity 方案,它能不能写出更好的代码来。没想到这个决定耗了老夫两天的光景。开始阶段,跟 AI 来回讨论,形成了一个看上去可行的类,于是把 Android Studio 打开,用工程向导生成了一个空 activity 的测试工程,然后再把 AI 输出的 activity 类替换进来。编译通过后运行,拖动是可以拖动,但有个问题,就是“遥控器”之外的区域怎么都不能透明,不是白就是黑。改了总有十几个版本,就是没办法。一个有实操经验的老程序员,加上全球最强大脑之一,竟然搞不定这么个小问题,这就很尴尬了。尽管 Grok 的方案使用了 Dialog 而不是之前的 ViewGroup,但其实核心原理并无二致,何至于此?
最后不堪其扰,我计划把这个类从刚刚创建的测试工程搬到之前那个已经证明可以透明的工程里跑一下,做个对比。这么一试,神了,Grok 版本的 activity 也是可以透明的。然后跟 Grok 一起分析探讨这个问题的可能性,推测大概是依赖库或者构建脚本环境的问题。为了方便两相比较,老夫还把新测试工程的构建脚本让 Windsurf 从 Kotlin 转成了 Groovy。在不停清理依赖,构建、测试,再不停变更依赖库的版本号,构建、测试的反反复复若干轮回后,发现了元凶,是 Compose。重点来了,只要在构建脚本里应用了 Compose 插件,并加入了其所必须的两个依赖库,哪怕你的代码一行不改,而且也没有使用跟 Compose 相关的特性,那个 activity 它运行起来,就是会不透明!这坑爹的玩意儿!!!
上面说了一大堆,有点跑题了,赶紧拽回来。想要引出的是,在 ViewGroup 和 Dialog 两个版本里,都有一长段代码,是用于实现拖拽的,逻辑完全相同,只是目标类型的差异。既然如此,而且它们又都阴差阳错汇到了一个工程里,这种大段重复代码(虽然严格意义上不能算是)看着实在碍眼,“提取公因式”的思维就按捺不住了。但如何抽象还是废了点劲的,因为 ViewGroup 跟 Dialog 在继承关系上毫无血缘。好在到了,老夫还是硬生生杀出一条血路,抽象出了一个 Movable 的抽象类,用它作为模板参数写了一个工具类,既可以操作视图组,也可以操作对话框。这段代码不仅实现了最开始进行复用的目的,还远远超越了当初仅着眼于当前使用的类的范围,成了一个可以直接拿走复用的独立类。颇有那么点“功在当代、利在千秋”的意味。于是,它就又作为集物癖的一件新“物”,出现并保留了下来。