1、机型适配介绍
1.1 Android中机型适配出现的原因(系统碎片和屏幕碎片)
Android平台的诞生为手机智能化的普及立下汗马功劳,但其最大的缺点也越来越凸显,那就是碎片化严重:设备繁多、品牌众多、版本各异,芯片、摄像头、分辨率不统一等等,这些都逐渐成为Android系统发展的障碍,碎片化严重不仅造成Android系统混乱,也导致Android应用隐形开发成本的增多。
下面这张图片所显示的内容足以充分说明当今Android系统碎片化问题的严重性,因为该图片中的每一个矩形都代表着一种Android设备。
1.2机型适配思路
手机适配主要包括三个方面:语言适配(优步)、屏幕适配(尺寸 方向切换)、SDK平台(5.0以上() 6.0 4.2 3.0 )的适配
2、屏幕的相关概念
//获取屏幕信息
public void getScreenInfo() {
//获取屏幕信息
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
// DisplayMetrics dm = getResources().getDisplayMetrics();
StringBuilder sb = new StringBuilder();
//屏幕密度
float density= dm.density;
//dpi
int dpi= dm.densityDpi;
//屏宽
int w =dm.widthPixels;
//屏高
int h =dm.heightPixels;
//字体在屏幕中的密度
float sd =dm.scaledDensity;
sb.append("密度=").append(density).append("\n");
sb.append("dpi=").append(dpi).append("\n");
sb.append("屏宽=").append(w).append("\n");
sb.append("屏高=").append(h).append("\n");
sb.append("字体密度=").append(sd).append("\n");
Log.d("ytmfdw", sb.toString());
}
2.1 屏幕尺寸
屏幕尺寸屏幕尺寸指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米比如常见的屏幕尺寸有:2.4、2.8、3.5、3.7、4.2、4.7、5.0、5.5、6.0等
2.2屏幕分辨率
屏幕分辨率是指在横纵方向上的像素点数,单位是px,1px=1个像素点。一般以横向像素数*纵向像素数表示,如1920*1080。
2.3屏幕像素密度及密度比值
获取屏幕信息
屏幕像素密度是指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写。屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小。一英寸里面有160个像素,这个屏幕的像素密度就是160dpi。
2.4 尺寸单位dp、dip、sp、px
px:像素。我们应该是比较熟悉的,前面的分辨率就是用的像素为单位,大多数情况下,比如UI设计、Android原生API都会以px作为统一的计量单位,像是获取屏幕宽高等。
dip和dp是一个意思,都是DensityIndependent Pixels的缩写,即密度无关像素,上面我们说过,dpi是屏幕像素密度,假如一英寸里面有160个像素,这个屏幕的像素密度就是160dpi,那么在这种情况下,dp和px如何换算呢?在Android中,规定以160dpi为基准,1dip=1px,如果密度是320dpi,则1dip=2px,以此类推。
sp,即scale-independent pixels,与dp类似,但是可以根据文字大小首选项进行放缩,是设置字体大小的御用单位。
2.5 mdpi、hdpi、xhdpi、xxhdpi
mdip 160dpi 1 标准
hdpi 240dpi 1.5
xhdpi 320dpi 2
xxhdpi 480dpi 3
xxxhdpi 640dpi 4
Ldpi 120dpi 1dp---0.75px
其实之前还有个ldpi --->0.75,但是随着移动设备配置的不断升级,这个像素密度的设备已经很罕见了,所在现在适配时不需考虑。
mdpi、hdpi、xdpi、xxdpi用来修饰Android中的drawable/mipmap文件夹及values文件夹,用来区分不同像素密度下的图片和dimen值。
Google对mdpi、hdpi、xdpi、xxdpi官方标准:
在进行开发的时候,我们需要把合适大小的图片放在合适的文件夹里面。下面以图标设计为例进行介绍。
图片的选择规则:
320 dpi mdpi
Android系统寻找图片的步骤是这样的:
若果在对应的dpi目录中找到,原图使用
如果在高级dpi目录找到,按照比例缩小
在低级dpi找到,按照比例放大
1.去屏幕密度对应的目录去找。如果找到就拿来用。
2.如果没找到,就去比这个密度高一级的目录里面去找,如果找到就拿来用。会自动缩小
3.如果当前hdpi xhdpi
4.如果没找到就继续往上找。以此类推。
5.如果到了Xxxhdpi目录还没有找到的话,就会去比自身屏幕密度低一级的目录去找,如果低一级的目录>=hdpi,找到了就拿来用。
6.如果没找到, 就去mdpi目录去找, 如果找到了,就拿来用,会放大。
7.如果没找到,就去默认的drawble目录里去找, 如果找到了就拿来用。
8.如果没找到,再去最低的ldpi目录里去找。如果找到了,就拿来用。
9.如果没找到, 那就是没找到了, 图片无法显示。(不过一般不会出现这种现象,因为如果每个目录都没有这个图片的话,你是编译不过的)
这里有两点需要注意:
① 首先会去比自己密度高的目录里去找,这是因为系统相信,你在密度更高的目录里会放置分辨率更大的图片,这样的话这个图片会被缩小,但同时显示效果不会有损失,但是如果优先去低一级别的目录去找的话,找到的图片就会被放大,这样的话这个图片就会被拉扯模糊了。同一张图片,你在mdpi和xxhdpi目录各放了一份,这个应用你现在运行在hdpi的手机上,那应用会选择哪张图片呢。答案是xxhdpi目录里的。即便hdpi离mdpi更近一点!
② 如果在mdpi里找不到是不会直接去ldpi里找的, 而是先去默认的drawble目录里找,这是因为drawble目录和drawble-mdpi是一个级别的。
3、机型适配的解决方案
3.1屏幕适配(相对布局、尺寸限定符、最小宽度限定符、屏幕方向限定符、自动拉伸位图)
支持所有屏幕尺寸
1、 使用wrap_content、match_parent
要确保布局的灵活性并适应各种尺寸的屏幕,应使用“wrap_content”和 “match_parent” 控制某些视图组件的宽度和高度。
使用”wrap_content”,系统就会将视图的宽度或高度设置成所需的最小尺寸以适应视图中的内容,而”match_parent”(在低于 API 级别 8 的级别中称为”fill_parent”)则会展开组件以匹配其父视图的尺寸。
如果使用”wrap_content”和”match_parent”尺寸值而不是硬编码的尺寸,视图就会相应地仅使用自身所需的空间或展开以填满可用空间。此方法可让布局正确适应各种屏幕尺寸和屏幕方向。
2、 使用线性布局,并使用weight属性。
我们可以使用这个属性来按照比例对界面进行分配,完成一些特殊的需求。
关于weight属性的理解:占用剩余空间(宽度或高度)的比重。使用这个属性的时候一般把宽或高的尺寸设置为0dp。
3、 使用相对布局。
注意:禁用绝对布局(已经被google完全抛弃)
4、 尽量用dp/sp
l 尺寸限定符(layout-large layout-large-land)
上面所提到的灵活布局或者是相对布局,可以为我们带来的优势就只有这么多了。虽然这些布局可以拉伸组件内外的空间以适应各种屏幕,但它们不一定能为每种屏幕都提供最佳的用户体验。因此,我们的应用不仅仅只实施灵活布局,还应该应针对各种屏幕配置提供一些备用布局。
我们可以通过使用配置限定符,在运行时根据当前的设备配置自动选择合适的资源了,例如根据各种屏幕尺寸选择不同的布局。
很多应用会在较大的屏幕上实施“双面板”模式,即在一个面板上显示项目列表,而在另一面板上显示对应内容。平板电脑和电视的屏幕已经大到可以同时容纳这两个面板了,但手机屏幕就需要分别显示。因此,我们可以使用以下文件以便实施这些布局:
可以在不同的layout目录中放置同名但布局内容不同的布局。这样在不同尺寸的手机中系统会自动匹配相应的布局。
layout | 默认屏幕 |
layout-small | 小屏幕 |
layout-large | 大屏幕 |
特大屏幕 |
官方文档:https://developer.android.com/training/basics/supporting-devices/screens.html
翻译后:
尺寸 | api版本 |
4.664761516 | 5 |
9.329523032 | 4.3 |
官方文档:
翻译:
RES /layout/ my_layout.xml //适用于布局进行正常的屏幕尺寸(“默认”)
RES /layout-large/ my_layout.xml //适用于布局大尺寸屏幕
RES /layout-xlarge/ my_layout.xml //适用于布局预算外大尺寸屏幕
RES /layout-xlarge-land/ my_layout.xml //适用于布局超大横向
上面是定义广义大小布局资源适配的一个范围,大家可以根据自己的设备知道系统会匹配那个文件的布局。如果手上有个山寨华为的卖的比较火的mediapad,大家知道分辨率1280*800 密度尺寸7寸通过勾股定了和分辨率可以得出其像素密度为
(勾股定理得到斜边的分辨率/尺寸) = 215.69。然后根据dp=px/(dpi/160),可以得出最小边的DP值为593.471。所以这个设备系统会匹配layout-large这个资源布局文件。
l 屏幕方向限定符
用法和尺寸限定符差不多
当手机横屏的时候。系统会自动去layout-land/ 目录中找布局资源
双面板布局的设置步骤:
第一步:创建布局文件
1.res/layout创建竖屏布局(只放TitleFragment)
2.res/layout-land创建横屏布局(放TitleFragment和内容的容器)
第二步:根据屏幕方向添加Fragment
//在Activity的onCreate中判断当前横竖屏状态。
//横屏
getResources().getConfiguration().orientation== Configuration.ORIENTATION_LANDSCAPE
//竖屏
getResources().getConfiguration().orientation== Configuration.ORIENTATION_PORTRAIT
l 最小宽度限定符(layout-sw600dp 3.2以前的系统不支持最小宽度限定符)
720px*1280px 大 160dpi mdpi 720dp*1280dp
720px*1280px 小 320dpi xhdpi 360dp*640dp
l 720px*1280px 240dpi(hdpi 1dp--1.5px) 720/1.5*1280/1.5 480dp*853dp
android开发时会涉及到多屏幕适应的问题。如果你的开发环境的sdk版本是android3.2以及以上的版本便可以用这个sw属性对设备进行区分。600dp的含义是:代表这个设备的最短的那一边。以我手上的平板为例(分辨率是1024x720,密度是1)。求这个设备的最短边。可能有人说是 720。这个答案是不正确的。答案是768,这个答案随着设备不同而不同。获取设备的最短边的代码是:
Configuration config =getResources().getConfiguration();
int smallestScreenWidth =config.smallestScreenWidthDp;
这样就获取了设备的最短边了。这个时候拿smallestScreenWidth 与600想比较就可以知道该设备能否读取里面的资源了。
官方文档翻译:
自动拉伸位图
使用9宫格图片(.9.png)
Android自带的工具:\sdk\tools\draw9patch.bat
3.2版本适配
版本适配方式一:
在Androidstudio 默认创建的项目中,会出现一个默认的values-v21
其中values-v14是我手动创建!
values-v14代表在API 14+的设备上,用该目录下的styles.xml代替res/values/styles.xml
values-v21代表在API 21+的设备上,用该目录下的styles.xml代替res/values/styles.xml
其中API 14+代表android 4.0 +
其中API21+代表android 5.0 +
如果对应的版本资源里面找不到,就回去默认的values中寻找!
版本适配方式二:
动态java控制:
对于 target sdk 对不同机器的兼容应该使用以下策略:
1.应该尽量使用较高版本的sdk去编译,在app的build.gradle中设置:
2.在使用相关的sdk api时,应该针对不同sdk去处理
总结:编译尽量用高版本,但是在处理特定函数时,应该根据build.version去处理区分处理
比如:沉浸模式只有在Android4.4及以上才能使用,那么当前编译版本的SDK一定要高于19,否则会找不到制定的版本常量或者相关的API。所以应该尽可能使用较高版本SDK开发。
那么代码中应该对当前的SDK_INT 版本做相应的判断:
// 判断当前SDK版本是否大于19---------具体对应的版本常量参考Build.VERSION_CODES类 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 沉浸 状态栏 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 沉浸 虚拟按键 // getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); }
//沉浸模式需要在setContentView之前调用 setContentView(R.layout.activity_main);
再比如:当前版本如果大于21使用黄色背景,否则如果大于14使用红色背景:
//如果当前SDK版本大于21
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { view.setBackgroundColor(Color.YELLOW);
//否则如果当前SDK版本大于14 } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { view.setBackgroundColor(Color.RED); }
3.3 语言自适应
这样就会多出一个中文的values资源文件出来。
也可以手动创建,只要记住对应国家的代码即可:如:
然后再手动创建strings.xml文件
那么系统适配字符的时候会首先去相应的目录下找,如果找不到就去默认的/res/values下找。(可以修改一下系统默认语言,再打开App测试)
提供一些相应的语言对照表: 1) 中文(中国):values-zh-rCN 2) 中文(台湾):values-zh-rTW 3) 中文(香港):values-zh-rHK 4) 英语(美国):values-en-rUS 5) 英语(英国):values-en-rGB 6) 英文(澳大利亚):values-en-rAU 7) 英文(加拿大):values-en-rCA 8) 英文(爱尔兰):values-en-rIE 9) 英文(印度):values-en-rIN 10) 英文(新西兰):values-en-rNZ 11) 英文(新加坡):values-en-rSG 12) 英文(南非):values-en-rZA 13) 阿拉伯文(埃及):values-ar-rEG 14) 阿拉伯文(以色列):values-ar-rIL 15) 保加利亚文: values-bg-rBG 16) 加泰罗尼亚文:values-ca-rES 17) 捷克文:values-cs-rCZ 18) 丹麦文:values-da-rDK 19) 德文(奥地利):values-de-rAT 20) 德文(瑞士):values-de-rCH 21) 德文(德国):values-de-rDE 22) 德文(列支敦士登):values-de-rLI 23) 希腊文:values-el-rGR 24) 西班牙文(西班牙):values-es-rES 25) 西班牙文(美国):values-es-rUS 26) 芬兰文(芬兰):values-fi-rFI 27) 法文(比利时):values-fr-rBE 28) 法文(加拿大):values-fr-rCA 29) 法文(瑞士):values-fr-rCH 30) 法文(法国):values-fr-rFR 31) 希伯来文:values-iw-rIL 32) 印地文:values-hi-rIN 33) 克罗里亚文:values-hr-rHR 34) 匈牙利文:values-hu-rHU 35) 印度尼西亚文:values-in-rID 36) 意大利文(瑞士):values-it-rCH 37) 意大利文(意大利):values-it-rIT 38) 日文:values-ja-rJP 39) 韩文:values-ko-rKR 40) 立陶宛文:valueslt-rLT 41) 拉脱维亚文:values-lv-rLV 42) 挪威博克马尔文:values-nb-rNO 43) 荷兰文(比利时):values-nl-BE 44) 荷兰文(荷兰):values-nl-rNL 45) 波兰文:values-pl-rPL 46) 葡萄牙文(巴西):values-pt-rBR 47) 葡萄牙文(葡萄牙):values-pt-rPT 48) 罗马尼亚文:values-ro-rRO 49) 俄文:values-ru-rRU 50) 斯洛伐克文:values-sk-rSK 51) 斯洛文尼亚文:values-sl-rSI 52) 塞尔维亚文:values-sr-rRS 53) 瑞典文:values-sv-rSE 54) 泰文:values-th-rTH 55) 塔加洛语:values-tl-rPH 56) 土耳其文:values--r-rTR 57) 乌克兰文:values-uk-rUA 58) 越南文:values-vi-rVN
|