相信对于移动开发的小伙伴来说,屏幕适配并不陌生,在项目中应该都会遇到这个问题。比如说,在小米手机上显示很正常,而在华为手机上显示就有问题,这就是屏幕适配的问题。下面,我们就来详细了解一下有关屏幕适配的知识点。
1、为什么要屏幕适配
因为Android系统的开放性,任何用户、开发者、OEM厂商、运营商都可以对Android进行定制,于是导致运行 Android 的设备多种多样,它们有着不同的屏幕尺寸和像素密度。 尽管系统可通过基本的缩放和调整大小功能使界面适应不同屏幕,但是,有些情况却是不尽人意,我们开发者就要进一步优化,确保我们的界面合理美观的展现在不同的手机屏幕上。
2、先了解几个基本概念
Android开发时用dp而不是px单位设置图片大小,是Android特有的单位
场景:假如同样都是画一条长度是屏幕一半的线,如果使用px作为计量单位,那么在480x800分辨率手机上设置应为240px;在320x480的手机上应设置为160px,二者设置就不同了;如果使用 dp为单位,在这两种分辨率下,160dp都显示为屏幕一半的长度。
安卓手机对于每类手机屏幕大小都有一个相应的屏幕像素密度:
3、屏幕尺寸、分辨率、像素密度三者关系

dp与px的转换:px = dp * (dpi / 160)


在Android中,规定以160dpi(即屏幕分辨率为320x480)为基准:1dp=1px
sp 与 dp 的区别
4、常规适配方案
-
布局组件的适配
-
布局的适配
-
代码适配 接口适配: 加载图片的时候
布局组件的适配
- 使用密度无关像素指定尺寸dp
- 使用相对布局或线性布局,不要使用绝对布局
- 使用wrap_content、match_parent、权重
- 使用minWidth、minHeight、lines等属性
- dimens使用
布局的适配
- 使用Size限定符
- 最小宽度限定符
- 使用布局别名
- 使用屏幕方向限定符
- 多套layout适配
图片的适配

- 普通图片和图标
- 自动拉伸位图: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 官方文档
ConstraintLayout和相对布局类似,都是靠上下左右的位置来进行约束,主要是自己多写,多练,掌握属性,属性非常多,下面介绍常用的属性,对吼提供一个资源,可以自己下载看看。
ConstraintLayout本身的基本属性
控制 View 最大尺寸和最小尺寸的属性,可以设置到 ConstraintLayout 上来控制 ConstraintLayout 的尺寸信息,尽量别用具体的数字来表示宽高,使
用wrap_content或者0dp。
相对定位的常用属性
layout_constraintLeft_toLeftOf
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
ConstraintLayout的边距常用属性
android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
ConstraintLayout的居中和偏移(bias)
app:layout_constraintHorizontal_bias="0"
app:layout_constraintVertical_bias="0"
ConstraintLayout角度
app:layout_constraintCircle=""
app:layout_constraintCircleAngle=""
app:layout_constraintCircleRadius=""
ConstraintLayout控制子View的宽高比
app:layout_constraintDimensionRatio="16:9"
layout_constraintDimensionRatio 控制子View的宽高比 除了上面三种设置 子 View 的尺寸以外,还可以控制 子 View 的宽高比。如果要使用宽高比则需要至少设置一个尺寸约束为 0dp,然后设置 layout_constraintDimentionRatio 属性 – float 值,代表宽度/高度 的比率 – “宽度:高度”这种比率值。
ConstraintLayout设置百分比
spread:尽可能扩展视图以满足每一方的约束。这是默认行为。 wrap:仅根据需要扩展视图以适应其内容,但仍允许视图小于约束所需的视图。因此,使用Wrap Content(上图)之 间的区别在于,将宽度设置为Wrap Content会强制宽度始终与内容宽度完全匹配; 而使用Match Constraints和 layout_constraintWidth_default设置为wrap也允许视图小于内容宽度。 percent:设置View的宽度为parent的比例值,比例值默认是100%,即宽度是match_parent。这个比例值通过属性 app:layout_constraintWidth_percent设置。
ConstraintLayout的链条布局
这个类似权重的属性
属性就介绍这么多,如果在使用过程中,还需要别的属性,可以在官方文档中查找,以下列举一些布局。
 
以上的布局文件在资源中心均可下载使用 :layout下载地址。
现在使用ConstraintLayout是一个趋势,可以自己适配项目,不需要再引入第三方的适配方案。

|