前言
有这么一个需求:通过一个PC的布局编辑器配置布局,发送到Android平板上展示。这个编辑器我们这里叫它“拖拉拽”系统。Android平板需要根据“拖拉拽”发送过来的控件信息,等比例的渲染控件,并根据附加的配置做一些事件的响应。
可以简单理解为低代码的儿童版啦,但本文的主题是等比例怎样实现的问题。提到等比例,笔者当时第一时间想到的是今日头条屏幕适配方案。所以本文就着重通过复习该方案,来记录一下需求的实现过程吧。
基础回顾
这里贴两篇文章回顾一下关于分辨率、DPI/PPI等基础知识:
还有搞Android屏幕适配都需要看的几条公式:
px = density * dp;
density = dpi / 160;
px = dp * (dpi / 160);
今日头条屏幕适配方案
先贴一下官方的推文:
该方案是一个百分比屏幕适配方案,区别于Google给出的一些根据分辨率的适配。这一类方案的优点在于可以在不同设备屏幕上呈现与设计图几乎一样的效果,而无需特别关注在开发中设置的某个控件的dp值。需要注意的是这里只是几乎而已。。。
如果您是没有了解过的话,可能会觉得这种方案好像很强大。但其实细想一下,这一类方案只是在开发成本和用户体验上的一种折中方案啦。亦或者说是简单粗暴而又带一点不负责任的适配方法,因为从用户使用的角度出发,如果自己使用的是一台大屏幕的手机,还是希望自己在一屏内的内容足够多且富有层次感,而不是还是和自己前两年的那台小屏幕一样嘛。只能说Android的碎片化,令这一切的开发成本大大提高了。
简单说了一下笔者的理解,方案的优缺点就不展开了,已经有很多文章了。这里可以参考:
回到方案本身,方案的实现其实就是利用了px = density * dp;这条公式。核心思想是:通过动态修改density,来改变您布局当中按照设计图设置的dp值计算出来的px值。
density的计算还是使用px = density * dp;,譬如优先对屏幕宽度适配,可以假设屏幕宽度为1080,设计图以宽度为360dp为基准。那么这里就能求出当设备宽度为1080px,360dp的density是多少,denstiy = px / dp,后续就可以通过计算出来的density自动换算布局。今日头条的做法是通过修改DisplayMetrics,来达到解析的xml满足效果的。
ps:需要注意的是,这里是优先对屏幕宽度适配的。因为理想状态下,设备宽高比和设计图宽高比应该是一致的。实际情况是设备的宽高比千奇百怪,如果严格按照宽宽比和高高比计算的话就会产生变形问题。可参考: 为什么说 AndroidAutoLayout 的设计有问题?
ps:这种计算方法是将density重新定义的,它绕开了dpi。
需求实现
为什么笔者在开头会说第一时间想到的是今日头条适配方案呢?因为需求上说的等比例其实就是一个百分比适配的问题嘛。区别在于笔者的目标不是计算一个density出来,而是需要计算出一个比例系数(其实也是那么回事啦)。
回头看需求本身,“拖拉拽”会以一块1920 * 1080的画布为基础,在此基础上进行控件的拖拉拽。所以这里就可以将1920类比成设计图的宽度,也就是px = density * dp;中的dp,那么px就是屏幕宽度了。所以会有公式:
// denstiy = px / dp
比例系数d = 屏幕宽度 / 画布宽度(1920)
举例说明
笔者用一台华为Matepad 11进行测试
尺寸:10.95英寸
分辨率:2560 x 1600,275每英寸像素
计算比例系数d
比例系数d = 2560 / 1920 = 1.3333333
PC端有一图片控件
// x, y 控件的起始坐标;w, h 控件的宽高。
{
"x": 100,
"y": 100,
"w": 100,
"h": 100
}
转换成真实显示 ps:这里笔者暂时用AbsoluteLayout.LayoutParams演示
val layoutParams = AbsoluteLayout.LayoutParams(
(1.3333333 * it.w).toInt(),
(1.3333333 * it.h).toInt(),
(1.3333333 * it.x).toInt(),
(1.3333333 * it.y).toInt())
这样就能得到几乎和PC端相同的效果了,大概草图就是开头贴出的效果:
屏幕宽高比问题
前面讲今日头条适配方案时也有提到过,该方案是优先适配宽度的。这里举个例子就知道发生什么问题了:
- PC端的画布为1920 * 1080,比例是16:9
- 笔者的Matepad 11屏幕宽高为2560 * 1600,显然不是16:9
这样显示出来就会变成下图这样了,红色虚线框的部分就是高度多出来的部分。因为是优先按宽度适配的,所以一般宽度是等比的。
解决的方案也只能是将父控件的宽高设置成16:9。当然了,这个就不是程序员能决定的事情了哈哈哈。
最后
本文通过一个小需求,重新复习一下今日头条屏幕适配方案,也简单重温了有关Android的屏幕适配。在开发时也是总被这些概念绕晕,所以笔者特意写一篇文章加以记录。