相信对于移动开发的小伙伴来说,屏幕适配并不陌生,在项目中应该都会遇到这个问题。比如说,在小米手机上显示很正常,而在华为手机上显示就有问题,这就是屏幕适配的问题。下面,我们就来详细了解一下有关屏幕适配的知识点。
1、为什么要屏幕适配
因为Android系统的开放性,任何用户、开发者、OEM厂商、运营商都可以对Android进行定制,于是导致运行 Android 的设备多种多样,它们有着不同的屏幕尺寸和像素密度。 尽管系统可通过基本的缩放和调整大小功能使界面适应不同屏幕,但是,有些情况却是不尽人意,我们开发者就要进一步优化,确保我们的界面合理美观的展现在不同的手机屏幕上。
2、先了解几个基本概念
- 屏幕尺寸:屏幕尺寸指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米 常见的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等
- 屏幕分辨率:屏幕分辨率是指在横纵向上的像素点数,单位是px,1px=1个像素点。一般以纵向像素*横向像素, 如1960\*1080,表示宽度方向上有1080个像素点,在高度方向上有1920个像素点
- 屏幕像素密度:屏幕像素密度是指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写。屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小。
-
密度无关像素: density-independent pixel ,叫 dp 或 dip,与终端上的实际物理像素点无关。可以保证在不同屏幕像素 密度的设备上显示相同的效果
-
独立比例像素: sp ,即 scale-independent pixels ,与 dp 类似,但是可以根据文字大小首选项进行放缩,是设置字体大 小的御用单位。
![](https://img-blog.csdnimg.cn/20201112225348231.png)
3、屏幕尺寸、分辨率、像素密度三者关系
dp与px的转换:px = dp * (dpi / 160)
在Android中,规定以160dpi(即屏幕分辨率为320x480)为基准:1dp=1px
sp 与 dp 的区别
- dp只跟屏幕的像素密度有关;
-
sp和dp很类似但唯一的区别是,Android系统允许用户自定义文字尺寸大小(小、正常、大、超大等等),当文字尺寸是“正常”时1sp=1dp=0.00625英寸,而当文字尺寸是 “大”或“超大”时1sp>1dp=0.00625英寸。类似我们在windows里调整字体尺寸 以后的效果——窗口大小不变,只有文字大小改变。
4、常规适配方案
-
布局组件的适配
-
布局的适配
-
代码适配 接口适配: 加载图片的时候
- 使用密度无关像素指定尺寸dp
- 使用相对布局或线性布局,不要使用绝对布局
- 使用wrap_content、match_parent、权重
- 使用minWidth、minHeight、lines等属性
- dimens使用
布局的适配
- 使用Size限定符
- 最小宽度限定符
- 使用布局别名
- 使用屏幕方向限定符
- 多套layout适配
图片的适配
- LOGO 图标
- 普通图片和图标
- 自动拉伸位图:Nine-Patch的图片类型
- 动画、自定义view、shape
5、今日头条适配方案
地址:今日头条github地址
原理:修改系统的DisplayMetrics,动态改变手机像素密度(density)
private static float sNoncompatDensity;// 系统的Density
private static float sNoncompatScaleDensity;// 系统的ScaledDensity
private static void setCustomDensity(Activity activity, final Application application){
final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
if(sNoncompatDensity == 0){
// 系统的Density
sNoncompatDensity = appDisplayMetrics.density;
// 系统的ScaledDensity
sNoncompatScaleDensity = appDisplayMetrics.scaledDensity;
// 监听在系统设置中切换字体
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
if(newConfig != null && newConfig.fontScale > 0){
sNoncompatScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
// 此处以360dp的设计图作为例子
final float targetDensity = appDisplayMetrics.widthPixels / 360;
final float targetScaledDensity = targetDensity * (sNoncompatScaleDensity/sNoncompatDensity);
final int targetDensityDpi = (int)(160 * targetDensity);
appDisplayMetrics.density = targetDensity;
appDisplayMetrics.scaledDensity = targetScaledDensity;
appDisplayMetrics.densityDpi = targetDensityDpi;
final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
}
注意:要在setContentView()方法之前使用,否则无效
优点:一个极低成本的 Android 屏幕适配方案,适配的成本很低,集成依赖,几行代码引入到项目,侵入性很低,就算后期,项目需要更换别的适配方案,也是非常方便的
缺点:头条适配这个对第三方的一些东西还是没法适配 / fresco,圆角失效 / 第三方View
6、smallestWidth 限定符适配方案
地址:GitHub地址
这一个方案,我们项目中目前使用的就是,效果也不错。
这套方案是上述几种方案中最接近完美的方案。
首先,从开发效率上,它不逊色于上述任意一种方案。根据固定的放缩比例,我们基本可以按照UI设计的尺寸不假思索的填写对应的dimens引用。
smallestWidth适配,或者叫sw限定符适配。指的是Android会识别屏幕可用高度和宽度的最小尺寸的dp值(其实就是手机的宽度值),然后根据识别到的结果去资源文件中寻找对应限定符的文件夹下的资源文件。在res/valus下面有很多个不同大小的资源文件,当系统识别到手机的smallestWidth值时,就会自动去寻找和目标数据最近的资源文件的尺寸。
优点:主要是针对资源文件,不会出现难以解决的问题,不会影响我们的业务代码逻辑,只要资源文件分布合理,就算SW没有找到完全对应的文件,它能向下兼容,寻找最佳的资源文件
缺点:多个dimens文件可能导致apk的体积变大,根据生成的dimens文件的覆盖范围和尺寸范围,apk可能会增大300kb-800kb左右,该最低支持版本应该都是4.0
7、宽高限定符适配
这一个方案,就是列举所有手机的宽高像素值,然后生成对应的values
有values-1920x1080, values-2560x1440,values-1280x800
设定一个基准的分辨率,其他分辨率都根据这个基准分辨率来计算,在不同的尺寸文件夹内部,根据该尺寸编写对应的dimens文件。
优点:按照设计稿上的尺寸填写相对应的dimens引用了,而当APP运行在不同分辨率的手机中时,系统会根据这些dimens引用去该分辨率的文件夹下面寻找对应的值。这样基本解决了我们的适配问题,而且极大的提升了我们UI开发的效率。
缺点:values下面要有想对应的文件匹配,比如1920x1080的手机就一定要找到1920x1080的限定符,否则就只能用统一的默认的dimens文件了,使用默认尺寸,就容易出错,导致UI不能适应手机的屏幕。
SW和宽高限定的区别:SW如果没有找到对应的valus,系统会向下寻找,找到最匹配的资源加载,宽高限定不是的,而是使用默认的values
8、约束布局ConstraintLayout的适配
这个布局相信很多人都听过,毕竟出来有几年,但是真正熟练使用的人并不多,我们项目组也在推进使用它,使用它可以自进行适配了,
- 简单介绍:ConstraintLayout 是一个ViewGroup,主要解决布局嵌套过多,适配,现在是官方默认的布局了。
-
优点:解决布局嵌套问题,ConstraintLayout可以按照比例约束控件位置和尺寸,能够更好地适配屏幕大小不同的机型
-
使用:implementation 'com.android.support.constraint:constraint-layout:1.1.3'
ConstraintLayout和相对布局类似,都是靠上下左右的位置来进行约束,主要是自己多写,多练,掌握属性,属性非常多,下面介绍常用的属性,对吼提供一个资源,可以自己下载看看。
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
app:layout_constraintVertical_bias="0"
app:layout_constraintCircleAngle=""
app:layout_constraintCircleRadius=""
layout_constraintDimensionRatio 控制子View的宽高比 除了上面三种设置 子 View 的尺寸以外,还可以控制 子 View 的宽高比。如果要使用宽高比则需要至少设置一个尺寸约束为 0dp,然后设置 layout_constraintDimentionRatio 属性 – float 值,代表宽度/高度 的比率 – “宽度:高度”这种比率值。
![](https://img-blog.csdnimg.cn/20201113144138287.png)
![](https://img-blog.csdnimg.cn/20201113144326514.png)
![](https://img-blog.csdnimg.cn/20201113144529153.png)
![](https://img-blog.csdnimg.cn/20201113144629366.png)
![](https://img-blog.csdnimg.cn/20201113144707637.png)
![](https://img-blog.csdnimg.cn/20201113144728596.png)
![](https://img-blog.csdnimg.cn/20201113144741547.png)
![](https://img-blog.csdnimg.cn/20201113144754874.png)
![](https://img-blog.csdnimg.cn/20201113145401158.png)
![](https://img-blog.csdnimg.cn/20201113145412576.png)
以上的布局文件在资源中心均可下载使用 :layout下载地址。
现在使用ConstraintLayout是一个趋势,可以自己适配项目,不需要再引入第三方的适配方案。