1、基本概念
- 屏幕尺寸:即手机屏幕对角线的长度 单位是英寸 inch。
- 屏幕分辨率:手机屏幕的宽 高像素点数 单位是px。一般设计给的设计稿也是以这个为单位的。
- dpi:屏幕像素密度 1英寸有多少像素。Android上会以这个来区分 mdpi hdpi xdpi 等资源文件夹。
- dp:密度无关像素 单位 dp,Android上推荐使用的单位
- desity:密度,表示 1dp等于多少px
mdpi、hdpi、xdpi、xxdpi用来修饰Android中的drawable文件夹及values文件夹,用来区分不同像素密度下的图片和dimen值。
那么如何区分呢?Google官方指定按照下列标准进行区分:
名称 | 像素密度范围 |
mdpi | 120dpi~160dpi |
hdpi | 160dpi~240dpi |
xhdpi | 240dpi~320dpi |
xxhdpi | 320dpi~480dpi |
xxxhdpi | 480dpi~640dpi |
2、解决方案
2.1 布局思路
※ 尽量使用使用wrap_content、match_parent、weight
weight属性的作用是决定控件在其父布局中的显示权重,一般用于线性布局中。
layout_weight属性值越小,则对应的layout_width或layout_height的优先级就越高,一般横向布局中,决定的是layout_width的优先级;纵向布局中,决定的是layout_height的优先级。
目前有两种方法:(1) 传统使用方法 :将layout_width和layout_height设置为fill_parent,通过 layout_weight属性控制控件的显示比例,当layout_weight越小,控件显示比例就越大。但是,如果布局中控件较多且显示比例不相同时,传统使用layout_weight属性的方法将比较麻烦。(2) 0px设值法:即使layout_width="0dp" 或layout_height="0dp",再结合layout_weight属性正比例控制控件的显示,该方法有效解决了当前Android开发中碎片化问题之一。
※ 使用相对布局,禁用绝对布局
2.2 宽高限定符适配
就是建不同手机宽高像素的values文件 如:values-480×320,然后找到一个基准分辨率,其他的分辨率都根据这个基准去把宽高等分。这样好处是我们只需要适配一个分辨率即可,缺点是命中率太低,如果没有找到当前运行的手机分辨率的文件夹,就会去拿默认dimens里的值,这样UI就可能变形了。
比如以480x320为基准分辨率
宽度为320,将任何分辨率的宽度整分为320份,取值为x1到x320
高度为480,将任何分辨率的高度整分为480份,取值为y1到y480
那么对于800480的分辨率的dimens文件来说,x1=(480/320)1=1.5px x2=(480/320)*2=3px
- 按比例计算其他分辨率下的尺寸
以480 x 320
为基准分辨率,在不同分辨率的文件夹下按比例缩放尺寸,例如800 x 480
:
<dimen name="x1">1.5px</dimen> 480/320 = 1.5 <dimen name="x2">3px</dimen> 1.5*2 = 3 ... <dimen name="x320 ">480 px</dimen> 1.5*320 = 480
优缺点:
- 致命缺点:容错率低,必须精准命中才能适配
- 风险点:增大包体积
2.3 smallestWidth 限定符适配方案
提示: 最小宽度值得是屏幕的两条边最小的一个,而不是指感官意义上的宽。
1、为每种需要适配的最小宽度提供一个文件夹
res ├──values ├──values-sw320dp ├──values-sw360dp ├──values-sw400dp ├──values-sw411dp ├──values-sw480dp
- 2、选定一个基准的最小宽度
举个例子,选定为360dp
为最小宽度,将宽整份为 360 份,得到以下尺寸:
<dimen name="x1">1dp</dimen> <dimen name="x2">2dp</dimen> ... <dimen name="dp_360 ">360dp</dimen>
- 3、按比例计算其他最小宽度下的尺寸
以360dp
为最小宽度,在不同最小宽度的文件夹下按比例缩放尺寸,例如sw480dp
:
<dimen name="x1">1.333dp</dimen> 480/360 <dimen name="x2">2.666dp</dimen> ... <dimen name="dp_360 ">480dp</dimen>
优缺点:
- 风险点:增大包体积
- 不足:不能自动支持横竖屏切换时的适配
- 不足:sp 也需要增加一份尺寸
2.4 今日头条适配方案
- 1、选定以宽或高维度适配(多数选择宽)
- 2、修改 DisplayMetrics#density
- 3、修改 DisplayMetrics#scaledDensity
- 4、监听系统设置中字体大小修改
代码示例:
DisplayMetrics appDisplayMetrics = mainPkgContext.getResources().getDisplayMetrics();
if (sNoncompatDensity == 0) {
sNoncompatDensity = appDisplayMetrics.density;
sNoncompatScaledDensity = appDisplayMetrics.scaledDensity;
// 当修改系统字体时,会回调此方法
mainPkgContext.getApplicationContext().registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration configuration) {
if (configuration != null && configuration.fontScale > 0) {
sNoncompatScaledDensity = mainPkgContext.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
// 320是设计图的宽度dp,可以根据自己的设计稿去修改
float targetDesity = appDisplayMetrics.widthPixels / 320;
float targetScaledDensity = targetDesity * (sNoncompatScaledDensity / sNoncompatDensity);
int targetDesityDpi = (int) (160 * targetDesity);
appDisplayMetrics.density = targetDesity;
appDisplayMetrics.scaledDensity = targetScaledDensity;
appDisplayMetrics.densityDpi = targetDesityDpi;
DisplayMetrics activityDisplayMetrics = context.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDesity;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
activityDisplayMetrics.densityDpi = targetDesityDpi;
优点:
侵入性极低,效果稳定