读书笔记,如需转载,请注明做者:Yuloran (t.cn/EGU6c76)php
Android开发者真的挺苦逼的,不只要适配常规分辨率,如 480x85四、720x1280、1080x1920、1440x2560,还要适配各类奇葩分辨率,如 720x1440、1080x2160...html
不一样分辨率和屏幕像素密度的手机,其宽高换算成dp后,有多是不一样的。以下表(Google规定:在160dpi的手机上,1dp = 1px):java
注:dpi 跟 ppi 一个意思,都表示每英寸多少个像素点android
分辨率(像素) | 像素密度(dpi) | 宽(dp) | 高(dp) |
---|---|---|---|
480x854 | 240 | 320 | 569.3 |
1080x1920 | 480 | 360 | 640 |
因此在1080x1920、480ppi的手机上,能够完整显示330dp宽的按钮,而在480x85四、240ppi的手机上,却显示不下。如何解决呢?Google机智地设计了一套资源限定符,只需新建一个values-hdpi文件夹,再建一个dimens.xml(名字随意),添加一句:数据库
<dimen name="your_widget_name">300dp</dimen>
复制代码
按钮宽度便会自动调整为300dp。bash
限定符 | 示例 | 含义 | 优先级 | since version |
---|---|---|---|---|
MCC 和 MNC | 示例: mcc310 mcc310-mnc004 mcc208-mnc00 ... |
移动国家代码 (MCC),(可选)后跟设备 SIM 卡中的移动网络代码 (MNC)。 例如,mcc310 是指美国的任一运营商,mcc310-mnc004 是指美国的 Verizon 公司,mcc208-mnc00 是指法国的 Orange 公司。 |
高 | API 1 |
语言和区域 | 示例: en fr en-rUS ... |
语言经过由两个字母组成的 ISO 639-1 语言代码定义,能够选择后跟两个字母组成的 ISO 3166-1-alpha-2 区域码(前带小写字母“r”)。 这些代码不区分大小写;r前缀用于区分区域码。不能单独指定区域。 |
↓ | API 1 |
布局方向 | ldrtl ldltr |
应用的布局方向。 ldrtl 是指“布局方向从右到左”; ldltr 是指“布局方向从左到右”,这是默认的隐式值。 |
↓ | API17 |
最小宽度 | swdp 示例: sw320dp sw360dp ... |
限定资源仅适用于最小屏幕宽度为Ndp的手机(不考虑屏幕方向)。 好比1080x1920 480ppi的手机,最小宽度限定符就是sw360dp; 720x1280 320ppi的手机,最小宽度限定符也是sw360dp。 |
↓ | API 13 |
可用宽度 | wdp 示例: w320dp ... |
限定资源仅适用于最小可用屏幕宽度为Ndp的手机(考虑屏幕方向)。 通常状况下,该限定符的值与swdp相同。 |
↓ | API 13 |
可用高度 | hdp 示例: h655dp ... |
限定资源仅适用于最小可用屏幕高度为Ndp的手机(考虑屏幕方向)。 计算N的时候必须减去StatusBar和NavigationBar的高度。 好比1080x2160 480ppi的手机,若StatusBar高度为25dp,NavigationBar高度为40dp,则N = 720 - 25 - 40 = 655,便可用高度限定符为h655dp。 |
↓ | API 13 |
屏幕尺寸 | small normal large xlarge |
small:QVGA,320x426(dp) normal:HVGA,320x470(dp) large:VGA,480x640(dp) xlarge:720x960(dp)【API 9】 |
↓ | API 4 |
屏幕纵横比 | nong notlong |
long:宽屏,如 WQVGA、WVGA、FWVGA notlong:非宽屏,如 QVGA、HVGA 和 VGA 彻底基于屏幕的纵横比(宽屏较宽),与屏幕方向无关。 |
↓ | API 4 |
圆形屏幕 | round notround |
round:圆形屏幕,例如圆形可穿戴式设备 notround:方形屏幕,例如手机或平板电脑 |
↓ | API 23 |
屏幕方向 | port land |
port:竖屏 land:横屏 |
↓ | API 1 |
UI 模式 | car desk television appliance watch |
car:设备正在车载手机座上显示 desk:设备正在桌面手机座上显示 television:设备正在电视上显示,为用户提供“十英尺”体验,其 UI 位于远离用户的大屏幕上,主要面向方向键或其余非指针式交互【API 13】 appliance:设备用做不带显示屏的装置 watch:设备配有显示屏,戴在手腕上【API 20】 |
↓ | API 8 |
夜间模式 | night notnight |
night:晚上 notnight:白天 |
↓ | API 8 |
屏幕像素密度 (dpi) | ldpi mdpi hdpi xhdpi xxhdpi xxxhdpi nodpi tvdpi anydpi |
ldpi:120dpi mdpi: 160dpi hdpi:240dpi xhdpi:320dpi【API 8】 xxhdpi:480dpi【API 16】 xxxhdpi:640dpi【API 18】 nodpi:放不想缩放的位图 tvdpi:213dpi【API 13】 anydpi:此限定符适合全部屏幕密度,其优先级高于其余限定符。 这对于矢量可绘制对象颇有用。【API 21】 六个主要密度之间的缩放比为 3:4:6:8:12:16(忽略 tvdpi 密度)。所以,9x9 (ldpi) 位图至关于 12x12 (mdpi)、18x18 (hdpi)、24x24 (xhdpi) 位图,依此类推。 若是您认为图像资源在电视或其余某些设备上呈现的效果不够好,而想尝试使用 tvdpi 资源,则缩放比例为 1.33*mdpi。例如,mdpi 屏幕的 100px x 100px 图像应该至关于 tvdpi 的133px x 133px。 |
↓ | API 1 |
触摸屏类型 | notouch finger |
notouch:有触摸屏 finger:设备有一个专供用户经过手指直接与其交互的触摸屏 |
↓ | API 1 |
键盘可用性 | keysexposed keyshidden keyssoft |
keysexposed:设备具备可用的键盘。若是设备启用了软键盘(不无可能),那么即便硬键盘没有展现给用户,哪怕设备没有硬键盘,也可使用此限定符。 若是没有提供或已经禁用软键盘,则只有在显示硬键盘时才会使用此限定符。 keyshidden:设备具备可用的硬键盘,但它处于隐藏状态,且设备没有启用软键盘。 keyssoft:设备已经启用软键盘(不管是否可见)。 若是提供了 keysexposed 资源,但未提供 keyssoft 资源,那么只要系统已经启用软键盘,就会使用 keysexposed 资源,而不考虑键盘是否可见。 |
↓ | API 1 |
主要文本输入法 | nokeys qwerty 12keys |
nokeys:设备没有用于文本输入的硬按键。 qwerty:设备具备标准硬键盘(不管是否对用户可见)。 12key:设备具备 12 键硬键盘(不管是否对用户可见)。 |
↓ | API 1 |
导航键可用性 | navexposed navhidden |
navexposed:导航键可供用户使用。 navhidden:导航键不可用(例如,位于密封盖子后面)。 不知道指的啥,反正不是底部的虚拟按键。那玩意儿须要根据各个厂商的定制方案去适配(监听广播或者读数据库)。 |
↓ | API 1 |
主要非触摸导航方法 | nonav dpad trackball wheel |
nonav:除了使用触摸屏之外,设备没有其余导航设施。 dpad:设备具备用于导航的方向键。 trackball:设备具备用于导航的轨迹球,如HTC G2。 wheel:设备具备用于导航的方向盘(不常见)。 |
↓ | API 1 |
平台版本(API 级别) | 示例: v3 v4 v7 ... |
从哪一个版本开始支持此限定符 | 低 | API 1 |
能够同时使用多个限定符,用"-"链接。如values-sw360dp-h655dp-port;网络
不区分大小写;app
同一类型的限定符只能出现一次;ide
限定符的使用顺序必须严格按照上表规定的优先级,不然会直接编译出错。如:布局
D:\HardwareDetection\app\src\main\res\values-port-sw360dp: Error: Invalid resource directory name
缘由:sw360dp优先级大于port
复制代码
不带任何限定符的资源文件夹,如values,是默认文件夹。该文件夹对应160ppi,即mdpi。当系统在其余资源文件夹下找不到对应资源时,便会使用默认文件夹中的资源。若默认文件夹也找不到,则抛出异常。
无需特殊适配的资源应当置于默认文件夹中。
屏幕像素密度限定符(如values-hdpi、values-xxhdpi),是惟一一个不会因冲突而被淘汰的文件夹。什么意思呢?就是说只要对应的资源存在,那么不论该资源放在哪一个dpi文件夹,系统都不会抛出异常(系统会优先选择高dpi文件夹下的资源)。而其余限定符则没有这个特权,若系统淘汰了与当前设备不匹配的限定符后找不到对应的资源,则会直接抛出异常:
Caused by: java.lang.RuntimeException: Binary XML file line #10: You must supply a layout_width attribute.
at android.content.res.TypedArray.getLayoutDimension(TypedArray.java:606)
复制代码
假设res下存在如下目录: drawable/ drawable-en/ drawable-fr-rCA/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/
用户手机的配置是: 语言区域 = en-GB 屏幕方向 = port 屏幕像素密度 = hdpi 触摸屏类型 = notouch 主要文本输入法 = 12key
那么该手机最终会采用哪一个文件夹下的资源呢?
淘汰与设备配置冲突的资源文件: drawable/ drawable-en/ drawable-fr-rCA/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/
查表,从最高优先级(MCC)开始,若该限定符存在,则淘汰其它全部不含此限定符的文件夹,不然顺位取下一个优先级的限定符,本例先淘汰非en文件夹(en-GB > port > hdpi > notouch > 12key): drawable/ drawable-en/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/
继续淘汰非port文件夹: drawable-en/ drawable-en-port/ drawable-en-notouch-12key/
仅剩一个文件夹了!因此该手机采用的就是 drawable-en-port 下的资源!
流程图:
注:限定符的优先顺序比与设备彻底匹配的限定符数量更加剧要。本例中,drawable-port-notouch-12key 包含更多相匹配的限定符仍然被淘汰了,就是由于它使用的限定符优先级比较低。
挑出须要特殊适配的资源。这里的资源主要指drawable、layout 和dimens;
设计师根据所需适配的分辨率分别切图和标注(像素为单位);
开发人员将设计师提供的切图放至对应的drawable文件夹下;
开发人员根据标注计算出对应的dp值,编写dimens.xml,并放至对应的values文件夹中。
有人问,这么多分辨率,我怎么知道是xxdpi仍是xxxdpi的文件夹?
So easy!
public class DisplayMetrics {
public static final int DENSITY_260 = 260;
public static final int DENSITY_280 = 280;
public static final int DENSITY_300 = 300;
public static final int DENSITY_340 = 340;
public static final int DENSITY_360 = 360;
public static final int DENSITY_400 = 400;
public static final int DENSITY_420 = 420;
public static final int DENSITY_560 = 560;
public static final int DENSITY_DEFAULT = 160;
public static final int DENSITY_DEVICE_STABLE = 0;
public static final int DENSITY_HIGH = 240;
public static final int DENSITY_LOW = 120;
public static final int DENSITY_MEDIUM = 160;
public static final int DENSITY_TV = 213;
public static final int DENSITY_XHIGH = 320;
public static final int DENSITY_XXHIGH = 480;
public static final int DENSITY_XXXHIGH = 640;
...省略若干行
复制代码
Android Studio中,双击shift,输入DisplayMetrics,查看class文件(不要查看java源码,注释太多了!),看看是否是应有尽有?
什么?你不知道手机的ppi是多少?easy!仍是这个类,用下面这个操做便可:
int densityDpi = Resources.getSystem().getDisplayMetrics().densityDpi; // 像素密度,值为320,480...
float density = Resources.getSystem().getDisplayMetrics().density; // dp 跟 px 的换算关系,值为1.5,2,3...
复制代码