Szhangbiao's blog

记录一些让自己可以回忆的东西

0%

开发板上二十多种分辨率屏幕适配总结

开发板开发相对于手机开发来说对应设备相对较低端,大部分都是横屏且不支持横竖屏切换,支持触屏的同时也要支持遥控器控制,分辨率更是多且杂,开发时除了关注性能外,屏幕适配也是一大难点。

背景

开发板相对于手机来说有哪些特点:

  • 开发板大多对应于音箱,机顶盒,学习机或投影仪等相对低端的设备
  • 开发板分辨率对应的 DPI 都较低,大部分集中在 mdpi(160), 少部分的有hdpi(240)xhdpi(320)
  • 多达 20+ 种分辨率,从 800480 到 19201080 不等,碎片化比手机还要严重
  • ARM 多集中在 1GB,512MB 和 2GB,少部分有 4GB,操作系统多是 Android 系统基础上修改的固件

误区

虽说都是 Android 开发,但开发板的开发跟手机开发的体验还是不一样的,开发版只有一块裸板,个别的是一体机,同时每人还配有显示屏,数据线,鼠标,遥控器,无线小 Wifi 等配套。板子在开发和测试之前互相替换使用,有的分辨率需要在一块板子上重新刷固件才能使用,所以所有分辨率都要过一边的话要耗费不少时间。

开发初期在认知上出现了一些误区:

  • 参照手机端的适配经验,觉得页面大致跟设计图匹配就行不用细致到完全一致。
  • 设计提供了 1024768、1280800 和 1280*720 三套设计图,觉得可以在代码层面抹平三套设计图之间的差异
  • 在接手应用的重做工作的前期便对整体开发做了详细的规划,没有理解 20+分辨率设备上适配的复杂性
  • 旧版的适配是一个分辨率一套资源,这样项目里就有很多drawable-xxxlayout-xxxvalues-xxx,个人认为这样开发后续不方便维护

后期问题就集中爆发出来:

  • 首先是设备需要等待使用,每次开发调整后都没法第一时间去验证
  • 除了分辨率多样性,还要考虑 Android 版本之间的差异,想要在代码层面抹平差异逐渐有心无力
  • 最开始确定的适配方案明显不适合开发板端的适配,后期逐渐陷入拆东墙补西墙的情况

调整

随着被测试不断提有关适配方面的 Bug,这时候停下来重新审视三套设计图和旧版的屏幕适配方案,思考了良久在此基础上做了调整,所以就有了现在的适配方案。

1.思考过程

之前的适配方案是基于最小宽度值的思想去适配的,不同的最小宽度定义不同values下的 dimens.xml,这套方案准对一套设计图的方案是满足的,三套设计图的话就不行了。
后面想到旧版的方案是一个分辨率一套资源,不同的分辨率向 1024768、1280800 和 1280*720 中宽高比最接近的设计图上靠,所以我改进了资源的引用方式和 Java 代码调整差异确定了最终的方案

2.方案确定

首先在布局方面针对不同的设计给出不同布局,默认给出的是 1024*768,另外两种设计分别在 xml 文件命名上添加对应分辨率的后缀,就像下面这样

1
2
3
fragment_xxx.xml // 1024*768
fragment_xxx_1280x800.xml // 1280*800
fragment_xxx_1280x720.xml // 1280*720

由于布局的根Layout使用的是ConstraintLayout,所以在FragmentonViewCreated生命周期方法回调里调整不同的布局样式

1
2
3
4
5
6
7
8
9
10
11
12
float screenRatio = ScreenUtils.getScreenAspectRatio(requireContext());
if (screenRatio <= Constants.SCREEN_RATIO_1280x720) {
ConstraintSet set1280x720 = new ConstraintSet();
set1280x720.clone(requireContext(), R.layout.fragment_xxx_1280x720);
TransitionManager.beginDelayedTransition(mBinding.clId);
set1280x720.applyTo(mBinding.clId);
} else if (screenRatio <= Constants.SCREEN_RATIO_1280x800) {
ConstraintSet set1280x800 = new ConstraintSet();
set1280x800.clone(requireContext(), R.layout.fragment_xxx_1280x800);
TransitionManager.beginDelayedTransition(mBinding.clId);
set1280x800.applyTo(mBinding.clId);
}

这个是页面层面,有些样式不能通过这种方式调整的封装在自定义Viewinflate不同样式的布局。

其次是尺寸和图片适配,尺寸的话还是dp,上面说过 20+分辨率分别向相近的设计上靠,于是就有了

1024*768 1280*800 1280*720
800*600 758*480 960*540
1024*768 800*480 1024*552
1280*1024 1024*600 1024*576
- 1136*640 1280*672
- 1280*752 1280*720
- 1280*768 1366*720
- 1280*800 1366*768
- 1440*900 1600*900
- 1680*1050 1920*1080

在符合同一设计图的分辨率范围内,要想达到相同的设计图效果,就要按照他们宽高中的最小值的比例去换算对应的dp值,拿1024*768这个案例来说:
如果 1024*768 分辨率在 DPI = 160 的情况下对应的dimens.xml

1
2
3
4
5
6
7
...
<dimen name="dp_m_2">-2.0000dp</dimen>
<dimen name="dp_m_1">-1.0000dp</dimen>
<dimen name="dp_1">1.0000dp</dimen>
<dimen name="dp_5">5.0000dp</dimen>
<dimen name="dp_10">10.0000dp</dimen>
...

那 800*600 分辨率在 DPI = 160 时的dimens.xml则是

1
2
3
4
5
6
7
...
<dimen name="dp_m_2">-1.5625dp</dimen>
<dimen name="dp_m_1">-0.7812dp</dimen>
<dimen name="dp_1">0.7812dp</dimen> // 1 * (600/768)
<dimen name="dp_5">3.9062dp</dimen>
<dimen name="dp_10">7.8125dp</dimen>
...

这里我们只要每个分辨率分别维护一套dimens.xml即可

图片适配的话还是按照之前最小宽度值的方式来适配,我们这里采用了.9 图的方式来减少图片的数量,不过还是要维护三套图。

3.辅助工具

每个分辨率生成对应的dimens.xml这里使用了ScreenMatch插件,首次使用会在项目根目录下生成相应的配置文件
默认的基础dp文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- screenMatch_example_dimens.xml -->
<resources>

<!-- 自定义的dp或sp -->
<dimen name="card_common_margin">@dimen/dp_15</dimen>

<!-- 通用的dp和sp -->
...
<dimen name="dp_m_2">-2dp</dimen>
<dimen name="dp_m_1">-1dp</dimen>
<dimen name="dp_0_5">0.5dp</dimen>
<dimen name="dp_1">1dp</dimen>
<dimen name="dp_1_5">1.5dp</dimen>
<dimen name="dp_2">2dp</dimen>
<dimen name="dp_2_5">2.5dp</dimen>
<dimen name="dp_3">3dp</dimen>
...
<!-- font size,you can add if there is no one -->
<dimen name="sp_6">6sp</dimen>
<dimen name="sp_7">7sp</dimen>
<dimen name="sp_8">8sp</dimen>
...
</resources>

和配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#screenMatch.properties

#基础dp值,主要参考设计图的标准来设置
base_dp=720
#需要匹配的设备的最小宽度值
match_dp=540,552,576,672,720,768,900,1080
#需要忽略的设备的最小宽度值
ignore_dp=240,320,384,392,400,410,411,432,480,533,592,600,640,662,752,800,811,820,960,961,1024,1050,1280,1365
#默认忽略的文件夹
# System default values is .gradle, gradle, .idea, build, .git
ignore_module_name=
#默认匹配的模块名
# Default value is 'app'.
...

不过它生成的dimens.xml还是在相应的values-sw(xxx)dp下,需要我们手动拷贝到对应分辨率的values-mdpi-xxx

4.开发提效

想要通过真实设备来查看 UI 效果需要等待特别长的时间,这里我们我们利用Android Studio的 xml 实时预览和AndroidTV模拟器上查看 UI 效果来提高开发效率
布局预览:我们主要看 1024x768、1280x1024、1280x800 和 1280x720 这四个分辨率效果就可以了,AS 提供的 Tablets 和 Desktop 里就有对应的分辨率
AndroidTV 模拟器:比较灵活,可以创建全部分辨率实例,其中创建时需要计算对应的 screen size 和创建后用 adb 命令修改 density 的值为 1,这样不用使用真实设备就可以快速查看代码效果。

当然 AndroidTV 模拟器只能使用 Android 高版本创建,低 Android 版本还是需要到真实设备上去测试。

总结

这一套组合拳下来基本上实现了现有分辨率的全适配,后续有新增分辨率适配,只要添加一份dimens.xml和注意图片适配就行。这样我们就从一个个的分辨率适配方案过渡到了解决一类适配的范畴,降低了维护成本。