前言
我的项目中,关于动态修改View的大小的技术方案进阶之路,在之前的文章中其实有讲到,强烈建议有兴趣的小伙伴可以去看看《产品需求驱动下的技术演进:动态缩放View的不同方案》,因为这是我们团队历经2-3年的技术迭代过程。
今天其实想聊聊在我们上线的方案之后,其实还有更简单也更有效的方案,在我们走过的那些路中有很多问题其实我们把问题想的太复杂了,用的方法也太复杂了,如果"跳出三界之外",再回过头看看,或许有更简单有效的做法。

回顾一下之前的方案
在之前的方案中,我们经历了从生成ImageView到手动处理所有的子view,以及遇到的性能问题、边界值计算问题、清晰度问题、视觉和真实大小不一致带来的问题等等各种问题,有相同想法的可以去看看。
把问题简单化
缩放的本质其实就是修改原本定义在layout中的数值,我们所做的操作其实也就是围绕着这一块做的,而我们的xml中的布局,在一开始就有默认的大小,也就是系统自己会处理好所有事情,那为什么我们不能依靠系统处理好呢?
在我们的布局中,一般来说布局文件长这样
kotlin
<LinearLayout
android:layout_width="@dimen/container_width"
android:layout_height="@dimen/container_height"
android:padding="@dimen/container_padding">
<TextView
android:layout_width="@dimen/child_width"
android:layout_height="@dimen/child_height"
android:margin="@dimen/child_margin"/>
</LinearLayout>
宽度、大小、字体都用dimens管理,那如果我们直接修改dimens的值或者直接替换成套的dimens,是不是就可以最简单的解决问题?
在res/values、res/values-swxxxdp(或自定义后缀,如values-size1、values-size2)下创建多套dimens.xml,每套文件中用相同的 dimen 名称定义不同的尺寸值,对应 10 种缩放比例。
例如:
kotlin
values-size1/dimens.xml(最小尺寸):
<<dimen name="container_width">100dp</</dimen>
<<dimen name="child_width">80dp</</dimen>
values-size2/dimens.xml(次小尺寸):
<<dimen name="container_width">120dp</</dimen>
<<dimen name="child_width">96dp</</dimen>
...(依此类推定义 10 套)
然后在使用的时候切换即可
kotlin
val config = Configuration(resources.configuration)
// 这里需要自定义 "qualifier"(但非标准 swNNNdp/语言/方向等很难生效)
val newContext = createConfigurationContext(config)
val view = LayoutInflater.from(newContext).inflate(R.layout.my_layout, null)
需要注意的是,采用这个方案切换后需要重新执行infalte的一套流程,单纯靠requestLayout是没有办法刷新触发新的dimens的
这个方案自然也有弊端,所有的"可能性"都需要提前预设,例如原本的缩放比例是1-100%自由缩放,如果采用在这个方案下就可能需要提前预设100套方案,那是不是应该修改相关的产品测的策略,将进度变成以每10%为一个可调整区间,这样只需要预设10套dimens即可
进阶的方案
动态切换dimens的思路是完全正确的,但是如果我们直接修改dimens可能会带来一些意想不到的问题:
只能按系统 qualifier(sw600dp、values-xxhdpi)自动匹配,很难在运行时手动切换。
并且切换时需要 模拟系统配置变更,可能导致 Activity 重建甚至全局刷新。
那既然知道问题,咱们就解决问题,首先在dimens的方案上,继续定义多套dimens,然后在主题中定义不同的style
xml
<!-- 默认尺寸 -->
<style name="AppTheme.Size1" parent="Theme.AppCompat.Light">
<item name="containerWidth">@dimen/size1_container_width</item>
<item name="textSize">@dimen/size1_text_size</item>
</style>
<!-- 第二套尺寸 -->
<style name="AppTheme.Size2" parent="Theme.AppCompat.Light">
<item name="containerWidth">@dimen/size2_container_width</item>
<item name="textSize">@dimen/size2_text_size</item>
</style>
然后在布局中不直接写 @dimen/...,而是写 ?attr/textSize,这样才会跟随 theme 切换
xml
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="?attr/textSize"/>
在自定义view中这样切换
kotlin
context.setTheme(R.style.AppTheme_Size2)
LayoutInflater.from(context).inflate(R.layout.my_custom_view, this, true)
切换后重新 inflate,布局里的所有 ?attr 会自动应用新 theme 对应的尺寸。
让AI参与进来
到这里,想硬扯一句AI,因为在处理这个方案的时候就在想,这个方案需要大量的预设,这是"笨办法",那现在我们有了AI,其实可以通过AI代替我们做这些繁琐复杂的事情,比如说直接通过Trae让他帮我们生成100套预设,而我们也就有更多的精力去用在更值得我们去思考的地方。
想想
技术成长的过程,其实就是不断折腾,不断试错。很多时候我们追求"优雅"和"通用",但过度设计往往带来新的复杂性。
由繁至简的过程,其实是一种不一样的体验,"笨办法"或许有时候才是真正聪明的做法,真正的经验往往不是"多高明的技巧",而是什么时候该停下折腾,选择最合适的平衡点。