从动态缩放自定义View,聊聊为什么不要把问题复杂化

前言

我的项目中,关于动态修改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套预设,而我们也就有更多的精力去用在更值得我们去思考的地方。

想想

技术成长的过程,其实就是不断折腾,不断试错。很多时候我们追求"优雅"和"通用",但过度设计往往带来新的复杂性。

由繁至简的过程,其实是一种不一样的体验,"笨办法"或许有时候才是真正聪明的做法,真正的经验往往不是"多高明的技巧",而是什么时候该停下折腾,选择最合适的平衡点。

公众号:柿蒂

相关推荐
用户4099322502121 天前
FastAPI秒杀库存总变负数?Redis分布式锁能帮你守住底线吗
后端·ai编程·trae
用户091 天前
Gradle Cache Entries 深度探索
android·java·kotlin
循环不息优化不止1 天前
安卓 View 绘制机制深度解析
android
叽哥1 天前
Kotlin学习第 9 课:Kotlin 实战应用:从案例到项目
android·java·kotlin
MrSYJ1 天前
Chat Memory你知道怎么用吗
llm·openai·ai编程
雨白1 天前
Java 线程通信基础:interrupt、wait 和 notifyAll 详解
android·java
飞哥数智坊1 天前
多次尝试用 CodeBuddy 做小程序,最终我放弃了
人工智能·ai编程
Lei活在当下1 天前
【业务场景架构实战】4. 支付状态分层流转的设计和实现
架构·android jetpack·响应式设计
诺诺Okami2 天前
Android Framework-Launcher-UI和组件
android
潘潘潘2 天前
Android线程间通信机制Handler介绍
android