“开发板”类APP如果做屏幕适配

前言

开发板这类设备主要以横屏为主且有的设备在交互上支持触屏和遥控器,屏幕适配相对于手机端的适配可简单也可复杂,如果要达到像扩展性高、维护简单和性能高的要求则需要花费一番功夫。

主流的屏幕适配方案有:

我目前所开发的应用的多分辨率适配方案是基于限制符适配 方案结合开发板 设备特点和三套UI设计图的适配方案,主要从图片适配和布局适配两方面进行。

UI适配的处理方式上做了一些转变,从一个分辨率设备对应一套资源(图片、布局和尺寸等)的方式到一套资源对应多个分辨率设备的方式,也可以理解为从一对一到一对多解决方案的转变,主要目的是减少资源重复,降低新增分辨率方案时的适配难度。

根据三套UI设计图,我们对二十多种分辨率的设备进行分类,分类的基准就是高/宽的比值更接近三套设计图中的哪一套,因此得出下面的表格:

1024*768(H/W≈) 1280*800(H/W≈) 1280*720(H/W≈)
1024*768(0.75) 1280*800(0.625) 1280*720(0.5625)
800*600(0.75) 758*480(0.633) 960*540(0.5625)
1280*1024(0.8) 800*480(0.6) 1024*552(0.5390)
- 1024*600(0.5859) 1024*576(0.5625)
- 1136*640(0.5633) 1280*672(0.525)
- 1280*752(0.58575) 1366*720(0.527)
- 1280*768(0.6) 1366*768(0.5622)
- 1440*900(0.625) 1600*900(0.5625)
- 1680*1050(0.625) 1920*1080(0.5625)

根据高/宽 的比值来划分不同设备的UI设计图参考归属,所以就有了范围 1024x768(>=0.75)、1280x800(>0.5625 & <= 0.625)和 1280x720(<= 0.5625)的划分 因此我们得到几个临界点值

java 复制代码
    public static final float SCREEN_RATIO_1280x1024 = 0.8f;
    public static final float SCREEN_RATIO_1024x768 = 0.75f;
    public static final float SCREEN_RATIO_1280x800 = 0.625f;
    public static final float SCREEN_RATIO_1280x720 = 0.5625f;
    public static final float SCREEN_RATIO_1366x720 = 0.528f;

1280*1024(0.8)、1366*720(0.527)和 1280*672(0.525)存在个别页面存在不协调的问题,因此单独创建布局做一些特殊处理。

图片适配

手机端的图片适配一般是根据设备的DPI来进行适配,他们之间的对应关系:

DPI 比值 举例
mdpi 1 16
hdpi 1.5 24
xhdpi 2 32
xxhdpi 3 48
xxxhdpi 4 64

手机端的设计图一般是按照720px为基准来设计,相应的png图片资源下载对应的倍率的图片放到对应的drawable-xxdpi下即可,如果是矢量图则更简单,用Android StudioVector Drawables功能做转换后,直接放到drawable下即可。

开发板的设备特点是大部分的设备的DPI=160,也就是mdpi,如果采用宽高限定符的适配方式会造成同一张图片存在于不同的分辨率的图片配置下,这无疑会增大安装包的大小,这也是我一开始就摈弃一个分辨率方案对应一套资源的适配方案的原因之一。

smallestWidth(简称sw)限定符 适配方案在这里是比较合适的,最小宽度值 的计算方式是min(widht,height)/density,根据分辨率分类后的设备特点定义不同的drawable-swXXXdp图片资源文件夹,然后以720768800为基准,在对应的drawable-swXXXdp里放入对应比例大小的png图片,少数设备DPI=223也就是hdpi(≈240)的,最小宽度值 计算后跟现有的drawable-swXXXdp冲突的,只要在资源文件夹后加上相应后后缀即可,例如drawable-sw600dp-mdpidrawable-sw600dp-hdpi,图片大小的比值按照上述表格里的比值适当放大或缩小即可。

图片适配最典型的适配场景就是控件聚焦 的图片适配,对于中大型的聚焦图片完全是按照以上介绍的适配方式进行,除此之外我根据聚焦控件的特点归纳了三种使用场景,封装了自定义View(可参考MarketFocusView),然后把这三种场景对应的聚焦图片转换成.9图,进一步的降低了这些相似图片的重复率。其实图片适配也可以不按照最小宽度值 来进行等比例的放大或缩小,只保留一份放在drawable-mdpi下即可,项目中很多小型的图片就是这样做的,拿图片占用内存来说,减少重复或相似图片或者缩小图片大小(图片占用内存=宽 每个像素占用字节)都能起到内存优化的作用,.9则稍微特殊一些,必须要按照最小宽度值来等比例的进行大小转换。

针对drawablelayoutvalues等资源文件夹,通过宽高限定符 或**sw限定符会产生很多带有相应 后缀的资源文件夹,对于同一类型的资源 宽高限定符** 和 sw限定符 不能同时存在,资源匹配的优先级是 sw限定符 > 宽高限定符

布局适配

跟图片的适配类似,本质上是同一个UI界面在不同分辨率下控件的尺寸、边距和字体大小等等比例的放大或缩小,所以就需要对基础的dpsp根据不同分辨率的最小宽度值 比例进行换算,这里用到了ScreenMatch插件,可以根据默认values下的dimen.xml文件和插件配置文件来生成对应分辨率的dimens.xml文件,它的生成规则也是按照最小宽度值 的方式来进行适配,不过因为需要适配三套UI设计图,也就是有720768800三个参考基准,因此需要根据上述的设备分类来生成三份插件配置文件,又因为dimens.xml里的dpsp是统一命名的,在尺寸资源的匹配规则是使用宽高限定符

有了dpsp的适配后,因为在Fragment中使用了ViewBinding,所以没办法在onCreateView方法里根据设备分类来inflate不同的布局,不过好在我所有布局的根 Layout 都是ConstraintLayout,根据ConstraintLayout的特性,可以在运行时使用ConstraintSet来匹配不同的布局样式

java 复制代码
// 默认的xml布局是参考1024x768的设计图实现的
float screenRatio = ScreenUtils.getScreenRatio(requireContext());

if(screenRatio >= Constants.SCREEN_RATIO_1280x1024) {
    ConstraintSet set1280x1024 = new ConstraintSet();
    set1280x1024.clone(requireContext(), R.layout.fragment_xxx_1280x1024);
    TransitionManager.beginDelayedTransition(mDetailsBinding.clId);
    set1280x1024.applyTo(mDetailsBinding.clId);
} else if (screenRatio <= Constants.SCREEN_RATIO_1366x720) {
    ConstraintSet set1366x720 = new ConstraintSet();
    float realRatio = ScreenUtils.getScreenRatioByWM(requireContext());
    set1366x720.clone(requireContext(), R.layout.fragment_xxx_1366x720);
    TransitionManager.beginDelayedTransition(mDetailsBinding.clId);
    set1366x720.applyTo(mDetailsBinding.clId);
} else if (screenRatio <= Constants.SCREEN_RATIO_1280x720) {
    ConstraintSet set1280x720 = new ConstraintSet();
    set1280x720.clone(requireContext(), R.layout.fragment_xxx_1280x720);
    TransitionManager.beginDelayedTransition(mDetailsBinding.clId);
    set1280x720.applyTo(mDetailsBinding.clId);
} else if (screenRatio <= Constants.SCREEN_RATIO_1280x800) {
    ConstraintSet set1280x800 = new ConstraintSet();
    set1280x800.clone(requireContext(), R.layout.fragment_xxx_1280x800);
    TransitionManager.beginDelayedTransition(mDetailsBinding.clId);
    set1280x800.applyTo(mDetailsBinding.clId);
}

一些控件的属性像TextViewtextSize的变动是没法通过ConstraintSet来设置的,需要额外使用代码来弥补这方面的不足。

RecyclerViewItem布局则是在自定义ViewGroup中根据分类inflate不同的xml布局实现

java 复制代码
    private void inflateLayout(Context context) {
        float screenRatio = ScreenUtils.getScreenRatio(context);
        if (screenRatio >= Constants.SCREEN_RATIO_1280x1024) {
            LayoutInflater.from(context).inflate(R.layout.layout_xxx_item_1280x1024, this, true);
        } else if (screenRatio <= Constants.SCREEN_RATIO_1280x720) {
            LayoutInflater.from(context).inflate(R.layout.layout_xxx_item_1280x720, this, true);
        } else if (screenRatio <= Constants.SCREEN_RATIO_1280x800) {
            LayoutInflater.from(context).inflate(R.layout.layout_xxx_item_1280x800, this, true);
        } else {
            LayoutInflater.from(context).inflate(R.layout.layout_xxx_item, this, true);
        }
    }

至此适配方案成型,后续只要对各个界面逐一进行上面的处理即可。

适配困境

开发板 上做屏幕适配在没有全局审视 的状态下或者不需要一次性适配20+分辨率的情况,一个分辨率方案对应一套资源的适配方式是比较合适的,从现在的时间点来看当时我在屏幕适配上也挣扎了一些时间。

主要原因有:

  • 前期不清楚手机端和开发板端的屏幕适配差异
  • 适配方案在开发阶段不断的调整,至少经历了3-4次改进
  • 设备紧张,适配在摸索阶段并不能很快的去验证,造成效率比较低

后续通过设备分辨率分类和创建TV模拟器查看UI效果才算真正解决屏幕适配的问题,这套适配方案缺点是在开发期间适配步骤比较繁琐,不过一旦完成配置后续几乎很少改动,并且大大降低了新分辨率的适配周期。

相关推荐
移动开发者1号2 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号2 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best6 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk7 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭11 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin
aqi0012 小时前
FFmpeg开发笔记(七十七)Android的开源音视频剪辑框架RxFFmpeg
android·ffmpeg·音视频·流媒体
androidwork14 小时前
深入解析内存抖动:定位与修复实战(Kotlin版)
android·kotlin
梦天201514 小时前
android核心技术摘要
android
高林雨露17 小时前
RecyclerView中跳转到最后一条item并确保它在可视区域内显示
android