屏幕适配方案
屏幕适配方案概述
1、系统布局
- match_parent, wrap_content
- LinearLayout 的 weight
- RelativeLayout
- ConstraintLayout 的 constraint
2、单位(dp/sp 方案)
- dp 一般适配控件宽高
- sp 一般设配字体
- dp 方案缺点:
相同分辨率的手机,尺寸不同,dpi 不同,density 不同,导致 1dp 代表的像素不一样,导致部分机型适配不友好
dp 适配差异性
3、.9 图
4、限定符
限定符就是 Android 在进行资源加载的时候会按照屏幕的相关信息对文件夹对应的名字进行识别,而这些特殊名字就是我们的限定符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
限定符分类:
屏幕尺寸
small 小屏幕
normal 基准屏幕
large 大屏幕
xlarge 超大屏幕
屏幕密度
ldpi <=120dpi
mdpi <= 160dpi
hdpi <= 240dpi
xhdpi <= 320dpi
xxhdpi <= 480dpi
xxhdpi <= 640dpi(只用来存放icon)
nodpi 与屏幕密度无关的资源.系统不会针对屏幕密度对其中资源进行压缩或者拉伸
tvdpi 介于mdpi与hdpi之间,特定针对213dpi,专门为电视准备的,手机应用开发不需要关心这个密度值.
屏幕方向
land 横向
port 纵向
屏幕宽高比
long 比标准屏幕宽高比明显的高或者宽的这样屏幕
notlong 和标准屏幕配置一样的屏幕宽高比
4.1 尺寸限定符
如 res/layout-large/main.xml
这种通过 large 限定符分辨屏幕尺寸的方法,适用于 android3.2 之前。在 android3.2 之后,为了更精确地分辨屏幕尺寸大小,Google 推出了最小宽度限定符。
4.2 宽高限定符
需要适配各种分辨率,values-aaaxbbb
如:values-1920x1080
- 缺点
分辨率太多,不好适配众多分辨率,还需要考虑虚拟导航键;需要精准命中分辨率,容错机制很差
4.3 屏幕方向限定符
给横屏、竖屏显示的布局不一样。就可以使用屏幕方向限定符来实现
1
2
3
res/values-sw600dp-land/layouts.xml:横屏
res/values-sw600dp-port/layouts.xml:竖屏
4.4 最小宽度限定符 dimens dp(sw 方案)
最小宽度限定符的使用和 large 基本一致,只是使用了具体的宽度限定。
如 res/layout-sw600dp/main.xml
。
最小宽度限定符适用于 android3.2 之后,所以如果要适配 android 全部的版本,就要使用 large
限定符和 sw600dp
文件同时存在于项目 res 目录
AS 插件 ScreenMatch 自动生成 dimens 文件夹
比例
百分比布局已经过时- 自定义 ratio 控件
hongyang 的AutoLayout,基于一个具体的设计图纸尺寸,根据目标设备的尺寸来进行比例适配
今日头条方案
修改 density,保证每个设备的屏幕总 dp 宽度不变
如以 360dp 为基准,然后根据 widthPixels/360dp = density,然后设置到系统 DisplayMetrics 去
AutoLayout
AutoLayout 坑
- 不继承 view 的控件例如:alertDialog 或者 popupwindow 无法适配
- 这个适配方案没有考虑 statusbar 和 navigation bar。在带虚拟按键的手机上面会很明显。谷歌 nexus 和华为大部分机型都是自带虚拟按键的,我们可以简单的测试一下。就加载一个圆形的头像,你会发现头像被压扁了
屏幕限定符
宽高限定符
px 适配
如以 1280x720 为基准,把所有的宽度切成 720 份,高度切成 1280 份,其他的尺寸基于这个基准等比缩放
1280x720,values-1280x720
如果是 800x400,values-800x480
根据不同的屏幕,x720 变量会代表对应的 px 值,其他的变量也一样
步骤
- 选一个尺寸为基准,一般是设计师的设计图纸,如
1920x1080
- 高就分 1920 份,宽就分 1080 分,其他的尺寸根据这个缩放
- 如 1280x720,将 1280 分成 1920 份,720 分成 1080 份
1
2
3
4
5
6
7
在1280x720分辨率高度1px = 1280/1920 = 0.667px
在1280x720分辨率宽度1px = 720/1080 = 0.667px
由于宽高比例一样,那么可以用一套
1x = 0.667px
1080x = 720px
1920x = 1280px
- UI 使用,直接根据设计师根据的基准分辨率 1920x1080 的设计图的标注,如按钮宽高 48x48px,那么直接在控件填 48x,系统会根据当前设备的尺寸自动寻找对应的 dimens 来加载。
最小宽度限定符 smallWidth(sw 方案)
sw 适配特点
- 目前主流设备都是 360dp,可以覆盖 90%
- 不能适配高,只是基于宽,或者只能基于高或者宽,why?请看:
https://github.com/JessYanCoding/AndroidAutoSize/issues/8
sw 适配 dimens 生成插件
https://github.com/mengzhinan/PhoneScreenMatch
使用:Android Studio 双击 shift,输入 ScreenMatch
sw 原理
基于 google 的 sw<N>dp
限定符规则,提供多套 dimens.xml
文件
dp 适配原理和 px 适配一样,只不过是拿 dp 值来等比缩放的而已
无论手机屏幕的像素多少,密度比值多少,但 80% 的手机的 dp 值 (widthPixels / density) 都为 360dp,所以对于这些手机,我们以 360dp 为基准,即只要写@dimen/dp_360 即可让控件横向沾满屏幕。那万一有些手机的这个值是 520dp 怎么办呢?加进去生成对应的 values 即可。
sw 适配步骤
- 根据具体的设计图纸尺寸来适配,如 1920x1080,使用直接用 px
- 找一个基准 dp,一般是找 360dp,即默认的 dimens 的值是根据 360dp 来的
- 按照 ui 设计图的设计图纸尺寸,如 1920x1080
- 计算 dimens,默认的就是 sw360
- 计算过程
1
2
3
4
5
6
7
8
9
# 360dp计算
分为1080份,
1份,px1 = 360dp/1080 = 0.33dp
px540 = 360dp/1080 = 180dp
# 410dp计算
分为1080份,
1份,px1 = 410dp/1080 = 0.3796dp
px540 = 410dp/1080 = 205dp
- 使用,直接在 ui 上填写基于 1920x1080 设计上标注的 px 对应的 dimens 值即可,不用转换为 dp
- 根据 dp,使用用设计标注的 px/density
案例 - 使用 px
默认 values/dimens.xml
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--base 360dp 1920x1080-->
<dimen name="px_200">66.67dp</dimen>
<dimen name="px_540">180dp</dimen>
</resources>
values-sw410dp/dimens.xml
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--base 410dp 1920x1080-->
<dimen name="px_200">75.926dp</dimen>
<dimen name="px_540">205dp</dimen>
</resources>
values-sw600dp/dimens.xml
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--base 600dp 1920x1080-->
<dimen name="px_200">111.111dp</dimen>
<dimen name="px_540">300dp</dimen>
</resources>
使用:
1
2
3
4
5
<me.hacket.assistant.common.widget.CapsTextView
android:text="适配dp 540 x 200"
android:background="@color/amber_200"
android:layout_width="@dimen/px_540"
android:layout_height="@dimen/px_200"/>
- 只适配了 360dp 效果:
很明显后面 2 个 411dp 的设备基本居中了
小米 max3:
 |
使用 dp
360dp/410dp
67dp/76.3056dp
默认 values/dimens.xml
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--base 360dp-->
<dimen name="dp_67">67dp</dimen>
<dimen name="dp_180">180dp</dimen>
</resources>
values-sw410dp/dimens.xml
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--base 410dp0-->
<dimen name="dp_67">76.3056dp</dimen>
<dimen name="dp_180">205dp</dimen>
</resources>
values-sw600dp/dimens.xml
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--base 600dp 1920x1080-->
<dimen name="dp_67">111.6667dp</dimen>
<dimen name="dp_180">300dp</dimen>
</resources>
使用:
1
2
3
4
5
<me.hacket.assistant.common.widget.CapsTextView
android:text="适配dp 540 x 200"
android:background="@color/amber_200"
android:layout_width="@dimen/dp_180"
android:layout_height="@dimen/dp_67"/>
小结:
无论是用 px 来划分多少份还是用 dp 来划分份数,都是一样的;使用上不一样而已,px 划分就可以直接填写设计图纸标注的 px,而用 dp 的话就需要手动转换为 dp 适配,还可能 px 转换为 dp 在生成的 dimens 找不到,只能找最近的 dimens 值
今日头条屏幕适配方案
原理
修改 density,保证每个设备的屏幕总 dp 宽度不变。
如以 360dp 为基准,然后根据 widthPixels/360dp = density,算出 density,然后设置到系统 DisplayMetrics 去
核心源码:
1
2
3
4
5
6
7
val baseDp: Float = 360.0F
var displayMetrics = resources.displayMetrics
var widthPixels = displayMetrics.widthPixels
var originalDensity = displayMetrics.density
var realDensity = widthPixels / baseDp\
displayMetrics.density = realDensity
特点
优点:
- 侵入性低,使用成本低
- 兼容性高,都是使用官方 API
- 可适配第三方库和系统库
- 没有性能损耗
缺点:
- 个别不是按照设计图来设计的需要单独处理
思考:
屏幕 dpi 更大 不是为了显示更多的内容么?
Reference
骚年你的屏幕适配方式该升级了!- 今日头条适配方案
https://juejin.im/post/5b7a29736fb9a019d53e7ee2
今日头条自定义 density 的改进版 pt
利用 pt 单位来承担
屏幕适配资源
尺寸限定符 -values- 分辨率
Android 屏幕适配 dp、px 两套解决办法
https://blog.csdn.net/fesdgasdgasdg/article/details/52325590Android dp 方式的屏幕适配 - 原理
https://blog.csdn.net/fesdgasdgasdg/article/details/82054971- Android 屏幕适配:最全面的解决方案
https://www.jianshu.com/p/ec5a1a30694b
比例/自定义控件
- 百分比控件
AndroidAutoLayout
https://github.com/hongyangAndroid/AndroidAutoLayout
sw 方案
Android 目前最稳定和高效的 UI 适配方案
https://juejin.im/post/5ae9cc3a5188253dc612842b给你一个全自动的屏幕适配方案(基于 SW 方案)!—— 解放你和 UI 的双手
https://tangpj.com/2018/09/29/calces-screen/
sw 生成 gradle 插件https://github.com/Tangpj/calces-gradle-plugin
今日头条方案
一种极低成本的 Android 屏幕适配方式
https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA
今日头条屏幕适配方案
- 系列
骚年你的屏幕适配方式该升级了!-smallestWidth 限定符适配方案
https://juejin.im/post/5ba197e46fb9a05d0b142c62骚年你的屏幕适配方式该升级了!- 今日头条适配方案
https://juejin.im/post/5b7a29736fb9a019d53e7ee2今日头条屏幕适配方案终极版正式发布!
https://juejin.im/post/5bce688e6fb9a05cf715d1c2
Android 技能树 — 屏幕适配小结
https://juejin.im/post/5b5315c8e51d45198565b172
Android 屏幕适配从未如斯简单(8 月 10 日最终更新版)
https://juejin.im/post/5b6250bee51d451918537021
- Android 屏幕适配终结者
https://juejin.im/post/5c18039d5188253b7e74987e
基于今日头条的 pt 方案
开源方案
- AndroidAutoSize
https://github.com/JessYanCoding/AndroidAutoSize
今日头条方案
- SmartLayout
https://github.com/weiyanjie/SmartLayout
今日头条方案