安卓(Android)奇葩机型UI适配方案
问题发现
最近在帮忙视觉走查还原,发现在N6手机上,整个UI给人的感觉会偏小,无论是控件的宽高,还是字体,和其他手机相比都会缩一圈的样子。最典型的的界面是账号登出界面,按钮占屏幕宽的比例明显不对劲。
如下图,左边是根据设计稿,还原出来的在普通手机上的效果,而右边的按钮部分,是适配前在N6手机上这一页面的效果。可以明显看出,整体都较左图偏小
例如按钮宽度这一参数,为330dp,而dp这个单位,本身就是安卓UI适配方案的一部分:
密度无关像素 (dp)
在定义 UI 布局时应使用的虚拟像素单位,用于以密度无关方式表示布局维度 或位置。
密度无关像素等于 160 dpi 屏幕上的一个物理像素,这是 系统为“中”密度屏幕假设的基线密度。在运行时,系统 根据使用中屏幕的实际密度按需要以透明方式处理 dp 单位的任何缩放 。dp 单位转换为屏幕像素很简单: px = dp * (dpi / 160)。 例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定义应用的 UI 时应始终使用 dp 单位 ,以确保在不同密度的屏幕上正常显示 UI。
无论屏幕的密度是多少,设置控件的参数单位为dp时应该总能适配才对。
问题原因
造成这一问题的原因其实很简单,设计同学的视觉稿,都是按照360dp乘640dp的标准来绘制的。而一般的手机,屏幕的宽高按像素/屏幕密度换算下来也就是这一值。但是诸如N6这款奇葩手机,屏幕密度为3.5,分辨率为1440 X 2560,换算成dp,宽高分别约为411dp和731dp。这样一来,就造成了不适配的情况了。
适配方案
针对这种情况,网上也有很多适配方案,例如:
Android 屏幕适配方案
Android 百分比布局库(percent-support-lib) 解析与扩展
我们的项目里目前使用的是官方推荐的:
Google官方屏幕尺寸支持方案
尺寸换算
这一方案的思路其实还是百分比的一种体现,例如一个36dp宽的按钮,在360dp宽的屏幕上,占比10%,那么如果在411dp宽的N6上要呈现一样效果的按钮,它的宽度应该为411dp*10%=41.1dp。
建立映射
在res目录下,建立两个文件夹,一是标准屏的文件夹,按照360dp为基础,即:values-sw360dp 下面的dimen.xml 是 1 - 500 一直是相等的:
sw360dpdimen.xml
1 2 3 4 5 6 7 8 9 | < resources > < dimen name = "dp_1" >1.00dp dimen > < dimen name = "sp_1" >1.00sp dimen > < dimen name = "dp_2" >2.00dp dimen > < dimen name = "sp_2" >2.00sp dimen > ... < dimen name = "dp_500" >500.00dp dimen > < dimen name = "sp_500" >500.00sp dimen >
resources > |
然后针对N6手机,建立一个411dp的文件夹,即values-sw411dp,数值按照比例来换算:411/360=1.14
sw411dpdimen.xml
1 2 3 4 5 6 7 8 9 | < resources > < dimen name = "dp_1" >1.14dp dimen > < dimen name = "sp_1" >1.14sp dimen > < dimen name = "dp_2" >2.28dp dimen > < dimen name = "sp_2" >2.28sp dimen > ... < dimen name = "dp_500" >570.83dp dimen > < dimen name = "sp_500" >570.83sp dimen >
resources > |
使用映射代替数值
建立好上述映射后,在布局中不再使用xxdp这样的数值,而是统一用@dimen/dp_xxx这样的形式:例如
1 | < button android:id = "@+id/account_logout" android:layout_width = "@dimen/dp_330" android:layout_height = "@dimen/login_btn_height" android:layout_gravity = "center" android:layout_marginleft = "@dimen/dp_15" android:layout_marginright = "@dimen/dp_15" android:layout_margintop = "@dimen/dp_37" android:background = "@drawable/qq_logout_bg_selector" android:text = "@string/account_logout" android:textcolor = "@color/colorPrimaryDark" android:textsize = "@dimen/game_item_title_size" > button > |
这样,在普通的360dp宽的设备上,dp_15对应的仍然是15dp,而在411dp宽的N6上,它的值就会映射为17.12dp,字体也是同一个道理,这样呈现出来的UI就和视觉稿一致了。
自动生成工具
奇葩的机型这么多,数值这么多,一个个手动加不太现实,网上也给出了自动化的工具:
DimenFilesBuilder
运行后,会自动生成文件夹和dimen.xml文件,放在项目里res目录下就好