⑴ android 屏幕适配
@[TOC](文章目录)
<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">
# 前言
<font color=#999AAA >使用工具Android studio,利用values文件下dimens.xml界面适配安卓屏幕</font>
<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">
<font color=#999AAA >提示:以下是本篇文章正文内容,下面案例可供参考
# 一、概念
1.屏幕分辨率单位是px,例如Android手机常见的分辨率:320x480px、480x800px、720x1280px、1080x1920px。
2.手机屏幕的密度:每英寸的像素点数,单位是dpi。
| 密度类型 |代表的分辨率(px)| 屏幕像素密度(dpi) | 1dp转换为px |
|:--------|:--------|:--------|:--------|
| 低密度(ldpi) |240x320|120|0.75|
| 中密度(mdpi) |320x480|160|1|
| 高密度(hdpi)|480x800|240| 1.5|
| 超高密度(xhdpi)|720x1280|320|2|
| 超超高密度(xxhdpi) |1080x1920|480|3|
3.由于android的机型屏幕大小品类太多了,有一些是不标准的,这时我们就需要单独去获取屏幕的分辨率和密度了。
# 二、获取屏幕的分辨率和密度
```java
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
float density = displayMetrics.density;
int densityDpi = displayMetrics.densityDpi;
int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels;
Log.e("123","密度:"+density+"---"+densityDpi);
Log.e("123","屏幕分辨率:"+width+"x"+height);
Log.e("123","安卓系统:"+android.os.Build.VERSION.RELEASE);
Log.e("123","手表型号:"+android.os.Build.PRODUCT);
```
# 三、SmallestWidth适配
**smallestWidth适配,或者叫sw限定符适配。指的是Android会识别屏幕可用高度和宽度的最小尺寸的dp值(其实就是手机的宽度值),然后根据识别到的结果去资源文件中寻找对应限定符的文件夹下的资源文件。**
**sw计算公式:sw = 屏幕宽度 / (dpi/160) 注:160是默认的**
**例如:屏幕宽度为1080px、480dpi 的sw = 1080/(480/160)**
# 四、生成 dimens 文件
1、 首先在 res 目录下新建各种尺寸的 values 文件 。文件名为:values-sw(你要适配屏幕的sw值)dp。
例如:
![code23](https://img-blog.csdnimg.cn/2020111311092374.PNG#pic_center)
注意:values文件下也生成 dimens文件
**生成dimens值工具类**
1、先生成标准的值。//value = (i + 1) * 1;
2、再用生成其他的值。 //value = (i + 1) * 需要生成的sw值/标准的sw值;
例如:value = (i + 1) * 160 / 320;
```java
public static void genDimen() {
StringBuilder stringBuilder = new StringBuilder();
try {
double value;
for (int i = 0; i < 500; i++) {
//value = (i + 1) * 1; //这里控制对应转换的值,如果是标准尺寸就一对一转换
//value = (i + 1) * 需要生成的sw值/标准的sw值; //这里控制对应转换的值
value = (i + 1) * 1
//value = (i + 1) * 160 / 320;
value = Math.round(value * 100) / 100;
//dp可改成sp
stringBuilder.append("<dimen name=\"size_" + (i + 1) + "\">" + value + "dp</dimen>\r\n");
}
if (stringBuilder.length() > 4000) {
for (int i = 0; i < stringBuilder.length(); i += 4000) {
if (i + 4000 < stringBuilder.length())
Log.e("123", stringBuilder.substring(i, i + 4000));
else
Log.e("123", stringBuilder.substring(i, stringBuilder.length()));
}
} else {
Log.e("123", stringBuilder.toString());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
```
示例:(我这是以sw320为适配的标准的,你们可改自己的标准)
1、sw320的样例
```java
<resources>
<dimen name="dimen_1">1.0dp</dimen>
<dimen name="dimen_2">2.0dp</dimen>
<dimen name="dimen_3">3.0dp</dimen>
<dimen name="dimen_4">4.0dp</dimen>
<dimen name="dimen_5">5.0dp</dimen>
<dimen name="dimen_6">6.0dp</dimen>
<dimen name="dimen_7">7.0dp</dimen>
<dimen name="dimen_8">8.0dp</dimen>
<dimen name="dimen_9">9.0dp</dimen>
<dimen name="dimen_10">10.0dp</dimen>
<dimen name="size_1">1.0sp</dimen>
<dimen name="size_2">2.0sp</dimen>
<dimen name="size_3">3.0sp</dimen>
<dimen name="size_4">4.0sp</dimen>
<dimen name="size_5">5.0sp</dimen>
<dimen name="size_6">6.0sp</dimen>
<dimen name="size_7">7.0sp</dimen>
<dimen name="size_8">8.0sp</dimen>
<dimen name="size_9">9.0sp</dimen>
<dimen name="size_10">10.0sp</dimen>
</resources>
```
2、sw160的样例
```java
<resources>
<dimen name="dimen_1">0.0dp</dimen>
<dimen name="dimen_2">1.0dp</dimen>
<dimen name="dimen_3">1.0dp</dimen>
<dimen name="dimen_4">2.0dp</dimen>
<dimen name="dimen_5">2.0dp</dimen>
<dimen name="dimen_6">3.0dp</dimen>
<dimen name="dimen_7">3.0dp</dimen>
<dimen name="dimen_8">4.0dp</dimen>
<dimen name="dimen_9">4.0dp</dimen>
<dimen name="dimen_10">5.0dp</dimen>
<dimen name="size_1">0.0sp</dimen>
<dimen name="size_2">1.0sp</dimen>
<dimen name="size_3">1.0sp</dimen>
<dimen name="size_4">2.0sp</dimen>
<dimen name="size_5">2.0sp</dimen>
<dimen name="size_6">3.0sp</dimen>
<dimen name="size_7">3.0sp</dimen>
<dimen name="size_8">4.0sp</dimen>
<dimen name="size_9">4.0sp</dimen>
<dimen name="size_10">5.0sp</dimen>
</resources>
```
3、xml界面控件使用样例
```java
<TextView
android:layout_width="@dimen/dimen_30"
android:layout_height="@dimen/dimen_30"
android:textSize="@dimen/size_20"
android:layout_margin="@dimen/dimen_10"
android:padding="@dimen/dimen_10">
```
<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">
# 总结
<font color=#999999 >提示:这里对文章进行总结:
如果你的app需要适配dpi较低的屏幕,最好以最小dpi的sw为适配的标准。
⑵ Android中分辨率,DPI,DP与PX对应关系
分辨率就是手机屏幕的像素点数。一般为屏幕的“宽×高”,例如分辨率有720×1280的手机设备,表示此屏幕在宽度方向有720个像素点,在高度方向有1280个像素点。
按屏幕对角测量的实际物理尺寸。为简便起见,Android 将所有实际屏幕尺寸分组为四种通用尺寸:小、 正常、大和超大,以英寸(inch)为单位。例如有个5寸的手机设备,是指对角线的尺寸,5寸×2.54厘米/寸=12.7厘米。
就是每英寸的像素点数,数值越高当然显示越清晰,通常 与“正常”或“高”密度屏幕相比,“低”密度屏幕在给定物理区域的像素较少。
在定义 UI 布局时应使用的虚拟像素单位,用于以密度无关方式表示布局维度 或位置。
密度无关像素等于 160 dpi 屏幕上的一个物理像素,这是 系统为“中”密度屏幕假设的基线密度。在运行时,系统 根据使用中屏幕的实际密度按需要以透明方式处理 dp 单位的任何缩放 。dp 单位转换为屏幕像素很简单: px = dp * (dpi / 160)。 例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定义应用的 UI 时应始终使用 dp 单位 ,以确保在不同密度的屏幕上正常显示 UI。
大多数情况下,确保应用中的屏幕独立性很简单,只需以适当的密度独立像素( dp 单位)或 "wrap_content" 指定所有 布局尺寸值。系统然后根据适用于当前屏幕密度的缩放比例适当地缩放位图可绘制对象,以 适当的大小显示。
如上表所示,现在我们在mdpi下设计了一个48x48的应用图标,那么在ldip下大小缩小0.75倍为36x36,在hdip下放大1.5倍为72x72,在xhdpi下放大2倍为96x96,在xxhdpi下放大3倍为144x144,在xxxhdpi下放大4倍为192x192.
android对于不同dpi设备提供了5个目录存放图片,使开发人员根据实际需要对图片适配。一般情况需要UI根据一种DPI密度、或分辨率的设备设计UI,根据不同dpi的比例出图,一般需要xhdpi、xxhdpi两套图,即2x、3x图。
计算相关公式:
px = dpValue * density + 0.5f;
dp = pxValue / density + 0.5f;
⑶ Android 关于"尺寸"的那些事(dp,dip,sp,pt,px...)
屏幕大小:屏幕大小是手机对角线的物理尺寸,以英寸inch为单位。比如我的Mix 2手机屏幕大小为5.99 inches,意味着我的屏幕对角线长度为5.99inches = 5.99 * 2.54 = 15.2146cm
分辨率:屏幕的像素点数,一般表示为a*b。例如某手机分辨率为21601080,意味着手机屏幕的竖直方向(长)有2160个像素点,水平方向(宽)有1080个像素点。
px :Pixels ,像素;对应屏幕上的实际像素,是画面中最小的点(单位色块),像素大小没有固定长度值,不同设备上1个单位像素色块大小不同。
这么说可能有点陌生,用屏幕分辨率来说,今年流行起来的“全面屏”分辨率是 2160*1080,但是你也可以发现,虽然很多全面屏手机分辨率一样,但是明显看得出来屏幕大小不一样,这也解释了“不同设备像素色块大小是不同的”。
pt :1pt=1/72 inch,用于印刷业,非常简单易用;
dpi :Dots Per Inch,每英寸点数;详见ppi
ppi :Pixels Per Inch,每英寸像素数;数值越大显示越细腻。计算式:ppi = 屏幕对角线像素数 / 屏幕对角线长度。
还是举全面屏的例子,分辨率2160*1080,屏幕大小是5.9inches,勾股定理可以得到对角线像素数大约是2415,那么ppi = 2415 / 5.99 = 403.
事实上dpi 和 ppi 一定程度上可以划等号,都表示像素密度,计算方式完全一致,只不过使用场景不一样。dpi中的dots点属于打印或印刷等领域,例如drawable 文件对应的就是dpi,而ppi中的pixel属于屏幕显示等领域
dp/dip : Density-independent Pixels,密度无关像素 - 基于屏幕物理密度的抽象单位。1dp等于 160 dpi 屏幕上的dpx,这是 系统为“中”密度屏幕假设的基线密度。在运行时,系统 根据使用中屏幕的实际密度按需要以透明方式处理 dp 单位的任何缩放 。dp 单位转换为屏幕像素很简单:px = dp * (dpi / 160)。 例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定义应用的 UI 时应始终使用 dp 单位 ,以确保在不同密度的屏幕上正常显示 UI。
如果看完文章还是觉得很懵,那么可以直接记住: 1dp单位在设备屏幕上总是等于1/160 inch。
sp :Scale-independent Pixels ,与 dp 单位相似,也会根据用户的字体大小偏好进行缩放。
首先我们放上源码中对尺寸单位的转换
可以看到,输入值类型为dp时,返回 value * DisplayMetrics.density,到这里我们可能会发懵:嗯?不对啊,前面我们不是通过px 和 dp 的换算公式来计算的么,怎么这里就简简单单乘了一个DisplayMetrics.density?不要慌,我们先看看源码中对DisplayMetrics.density的介绍。
源码注释中说到“在160dpi的屏幕下,density的值为1,而在120dpi的屏幕下,density的值为0.75”,我们可以大胆的猜测一下,120dpi下的density=0.75的原因是120dpi * 1 /160dpi=0.75。实际上,也就是这么回事。我们下面会仔细的分析。
需要补充一下,通常意义上Android 屏幕的密度,指的是像素密度dpi/ppi,对应于源码中的DisplayMetrics.densityDpi。
为什么引入dp?
Android 引入了dp这一单位,使得不论多大屏幕,多大dpi,显示的效果始终保持一致。
但是根据前面我们提到的px与dp的换算公式px = dp * (dpi / 160),很显然,由于相同分辨率但不同屏幕大小的设备dpi是不同的,导致px和dp的基本不存在一个固定的换算关系,为了方便屏幕适配,Android设置了6个通用的密度,换算px与dp时采取通用密度计算,而非设备实际的密度。
以下为6种通用密度,以及其最小的分辨率
得到上面通用密度之后,我们换算dp与px多了一种简便方式。前面我们提到Android将mdpi作为基准,此时1px = 1dp,又有px = dp * (dpi / 160),所以我们可以很容易的得到以下换算:
还记不记得前面源码中的density属性,实际上DisplayMetrics.density = dpi / 160 ,表示的就是在某个通用密度下dp与px的换算比(1dp/1px的值)
这部分其实和程序员自身已经关系不大了,毕竟参与工作之后这些都是UI人员的活儿了。不过鉴于现在我还只是一枚在校生,还是记下来以免自己遗漏吧。
建议在xhdpi中作图
原因嘛,首先现在主流分辨率是1080p,以及最近流行起来的全面屏18:9,而xhdpi对应720p,向低dpi兼容自然没问题,即便在xxhdpi中显示,也会有个不错的效果。而如果以1920*1080作图,显然图片素材占用的内存很大,而且也会增大应用安装包的大小。
只有一个原则:资源放入对应dpi的文件夹中,Android会机智的加载合适的资源。
以drawable资源为例:
我们平时开发小项目&对UI要求不高时,只使用一套xhdpi的资源就足够了,虽然这可能会导致在hdpi及以下的手机中有些卡顿,因为xhdpi的图片运行在hdpi及以下的手机上会比较吃内存,不过无伤大雅。
而如果不为图片资源犯愁时(有UI人员的支持,就是任性),就可以添加所有dpi的资源。当然,重点还是要满足ldpi:mdpi:hdpi:xhdpi:xxhdpi=3:4:6:8:12的规律。
好像说了不少废话,哈哈,大概就这么多吧。
⑷ Android中常见的单位ppi,dp,dpi,sp,px
在android 开发过程中,我们使用的单位比较少,一般情况下在描述字体大小的时候我们通常用sp,而在设置间距的时候我们用dp,除此之外很少再用到其他单位,而且很多时候我们用着用着就习惯了,也不去探究为什么这么写,可不可以用其他单位,每个单位到底代表着什么意思,所以说,习惯真的很可怕呀。今天,我们就来一探究竟,看看这些单位背后的含义。
像素即是屏幕上显示数据的最基本的点,在PS里面也是其最根本的单位,所有的图形都是在此基础上生成的,平时我们经常讲的手机屏幕分辨率就是以像素作为单位的,比如在android中我们经常说的手机像素是1080X1920,其实它所表达的意思是在该手机上面在横向上面有1080个像素点,在纵向上有1920个像素点。
在android中用来形式字体大小的单位,正常情况下会按照手机系统设置的文本大小来显示文字,但是同时也会与系统设置的文本保持一致,比如在有些老年机上面为了更好的操作手机有些人会将字体设置为较大字体,这个时候使用sp作为单位的字体也会随之变大,但是如果将字体大小的单位设置为dp,则不会随着系统字体的变化而变化。
在每次的手机厂商新品发布会上,我们都会听到关于手机的介绍,比如手机的屏幕分辨率,多大尺寸等等。而当我们知晓一个手机的屏幕分辩率和手机尺寸的时候,就可以计算出手机的物理像素密度,其计算公式为:
需要注意的是,PPI是Android手机物理像素密度,而非在Android开发过程中我们经常说到的像素密度。
屏幕密度与dpi密切相关,dpi是每英寸的点数。也就是说,密度越大,每英寸内容纳的点数就越多。
在android.util包下有个DisplayMetrics类可以获得密度相关的信息。最重要的是densityDpi这个成员,它有如下几个常用值:
DENSITY_LOW = 120
DENSITY_MEDIUM = 160 //默认值
DENSITY_TV = 213 //TV专用
DENSITY_HIGH = 240
DENSITY_XHIGH = 320
DENSITY_400 = 400
DENSITY_XXHIGH = 480
DENSITY_XXXHIGH = 640
dpi的值主要是通过displayMetrics获取的,获取方式为:
val densityDpi = resources.displayMetrics.densityDpi。
dp和dip是一样的,设备独立像素,这个和设备硬件有关,不同设备有不同的显示效果。而通常在做android项目的时候,为了适配市场上面众多的手机屏幕分辩率,我们一般都会采用dp。dp是Android基于物理设备的PPI抽象出来的一个单位。它是以160dpi的屏幕为基准定义的,在160dpi的屏幕上1dp=1px,那么由此我们就可以得出其计算公式:
换算公式:1dp = (屏幕ppi/160)px或者是px = (屏幕ppi/160)*1dp。举个例子:假设ppi = 320,那么1dp = 2px。
下面我们来演练一下:
如图所示,手机的屏幕分辩率为1080X1920,尺寸为5寸,从而计算得出PPI的值为440,再通过PPI计算出1dp 约等于3px。假设现在美工给的图上面有一个a图标,距离顶部的距离为30px,那么根据最终我们的换算结果可知,我们设置为10dp就可以达到完美的显示效果。
⑸ Android 屏幕适配
Android的屏幕大同小异,分辨率也是各种各样,手机App上的差异性还没那么明显,基本用Dp & weight就可以比较好的适配各种手机。但是在Pad上的表现就不尽如意,而且发现像华为Pad Pro这种高端设备,是可以通过系统设置去设置修改系统的density值,导致整个如果只用一套DpUI布局去实现,会出现很奇怪的UI效果,基本不能适配。这时候就需要对UI进行适配。通过资料查询,需要了解如下的几个概念
1.px,pixel 就是像素,最基本的真实显示单位
2.dp,dip, Density-independent pixel,设备的独立像素,1dp表示在屏幕像素点密度为160ppi时1px长度
3.ppi, pixel per inch ,每英寸对角像素点,这个是物理上的
4.dpi, dot per inch ,每英寸多少个点,这个是软件上的,这里的点跟像素点不同
5.sp: scale-independent pixel, 字体大小单位
简单换算就是
ppi =根号( 横屏像素点平方+纵屏像素点平方)➗对角线的长度,这个长度是一英寸
1dp = (dpi/160) px
然后有些不同尺寸手机的ppi可能是420, 430, 440, 450, 460.,由于物理ppi上是固定的,改变不了,为了适配,通过人为设置一个dpi,来规范这些差不多ppi值,使得这些相差差不多的屏幕都是通用一个dpi,也就是使用同一套设计方案。
一开始通过dp值来实现适配,是可以解决大部分适配问题,但是在遇到pad这种设备,由于是横屏,而且系统设置还可以修改density值,使得用一套固定屏幕(比如1280 * 800)的方向变得不是那么合适。
这时候想到可以通过Android中 dimens中定义dimen值,Android中可以通过sw去搜索对应的dimen值表来获取对应的配置,smallestWidth适配,sw限定符适配,只要我们把对应的表通过换算,得到一个新值,就可以得到在不同的density值中得到对应的dp值表,解决华为上一个设备对应不同density值的问题。
那么问题来了,如果去得到sw不同的dimens呢,网上的方法很多,有些自己写脚本,有些自己写程序生成,为了就是列举所有的值,一般1-1000dp,然后基于一个基准,比如360dp宽度,去换算出不同屏幕宽度的dimens值,这里我推荐Android Studio的插件ScreenMatch,先安装插件
然后在values中创建dimens文件夹,并创建dimens.xml,其中写上自己定义的dp值,如下
然后在该文件右键,选择screenmatch
插件就会生成一堆其他屏幕的dimens文件,并且自动生成1-800的其他dp值,基本满足开发中的定义,如果没有的话,就自行在这里定义,然后重新生成。
关于ScreenMatch的生成还有一个基准,就是基于那个dpi来生成,通过插件生成,在根目录会多出了两个文件,一个example, 一个config文件
这里我们看看properties文件,内容如下
其中base_dp=850就是基于850,然后可以通过match_dp去调整要适配的dpi值。
通过这方方式,会在dimens文件自动生成dimen文件
在网上看到,还可以通过修改density去修改,这种方式有空我在试试
⑹ android获取屏幕的分辨率方法有几种
获取屏幕分辨率的方式如下:
一、在activity中
1、DisplayMetrics dm = getResources().getDisplayMetrics();
int screenWidth = dm.widthPixels;
int screenHeight = dm.heightPixels;
2、DisplayMetrics dm = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(dm);
int screenWidth = dm.widthPixels;
int screenHeight = dm.heightPixels;
二、在service中
DisplayMetrics dm = new DisplayMetrics();
dm = getResources().getDisplayMetrics();
int screenWidth = dm.widthPixels;
int screenHeight = dm.heightPixels;
⑺ 如何取得Android设备的确切dpi值
无须API,VB本身的功能就能完成: 123456789Private Sub Form_Click() '点击窗体运行Dim dpi_x As Single, dpi_y As Single, px_twip As Singledpi_x = Me.ScaleX(Screen.Width, 1, 3) / Me.ScaleX(Screen.Width, 1, 5)dpi_y = Me.ScaleY(Screen.Height, 1, 3) / Me.ScaleY(Screen.Height, 1, 5)px_twip = Me.ScaleX(1, 3, 1)Print "DPI_X = " & dpi_xPrint "DPI_Y = " & dpi_yPrint "1像素 = " & px_twip & "缇"End Sub
⑻ Android 屏幕分辨率适配
Android屏幕分辨率千奇百怪,怎么让app在不同的分辨率的设备上“看起来一样”呢?
你也许还有以下疑惑:
这篇文章将会针对以上问题一一解答。
Pixels 我们看到屏幕上的图像由一个个像素组成,像素里包含色彩信息。
如常说的手机分辨率:1080 x 1920 指的是手机宽度可展示1080像素,高度可展示1920像素。
Pixels Per Inch 每英寸长度所具有的像素个数,单位面积内像素越多,图像显示越清晰。
ppi一般用在显示器、手机、平板等描述屏幕精细度。
Dots Per Inch 每英寸长度所具有的点数。
dpi一般用来描述打印(书本、杂志、电报)的精细度
density-independent pixels (device-independent pixels 我查了一下,官网更多时候使用前者,有的时候也显示后者),dip是缩写,也可以更简单些称作dp。该单位的目的是屏蔽不同设备密度差异,后面细说。
Scalable pixels 用于设置字体,在用户更改字体大小时候会适配。
澄清了基本概念,我们现在从一个例子开始说明以上单位之间的区别与联系。
布局文件里有个View,长宽都是200px,分别在分辨率为480(宽)x800(高)简称A设备、1080(宽)x1920(高)简称B设备,效果如下:
左边是A设备,右边是B设备。问题出来了,同样长宽都是200px,为啥A设备显示很大,B设备显示很小呢?你可能会说B设备的横向分辨率1080比A设备的480大,所以在B设备上看起来比较小。来看看A、B设备横向到底是多少英寸,怎么来计算呢?这时候就需要用到ppi了,既然知道横向的像素点个数,也知道每英寸能容纳的像素点,当然可以得知横向的尺寸了。
其中一种方式获取DisplayMetrics对象:
A设备宽度尺寸:480(px)/240(ppi)=2inch
B设备宽度尺寸:1080(px)/420(ppi)=2.5inch
可以看出,A、B设备尺寸差别不大。A设备ppi=240 B设备ppi=420,明显地看出B设备单位长度上比A设备能够容纳更多的像素,因此同样的200px,B设备只需要较小的尺寸就能够显示,因此在B设备上的view看起来比A设备小很多。
知道了问题的原因,然而显示的效果却不能接受。
我们总不能自己判断每个设备的ppi,然后计算实际需要多少像素,再动态设置view的大小吧,那layout里的静态布局大小就无法动态更改适应了。想当然的能有一个统一的地方替我们转换,没错!Android系统已经帮我们实现了转换。接下来就是dpi、dp出场了。
Android系统使用dpi来描述屏幕的密度,使用dp来描述密度与像素的关系。
A设备dpi=240
B设备dpi=420
Android系统最终识别的单位是px,怎么将dpi和px关联起来呢?,答案是dp。
Android规定当dpi=160时,1dp=1px,当dpi=240时,1dp=1.5px,依此类推,并且给各个范围的dpi取了简易的名字加以直观的识别,如120<dpi<=160,称作为mdpi,120<dpi<=240 称作hdpi,最终形成如下规则:
现在知道了dp能够在不同dpi设备上对应不同px,相当于中间转换层,我们只需要将view长宽单位设置为合适的dp,就无需关注设备之间密度差异,系统会帮我们完成dp-px转换。将我们之前的例子稍微更改,再看看效果验证一下:
通过上面对dp的了解,我们知道在设定view大小、间距时使用dp能最大限度地屏蔽设备密度之间的差异。可能你就会问了,那bitmap展示的时候如何适配不同密度的设备呢?
自定义view从磁盘上加载一张图片,并将之显示在view上,view的大小决定于bitmap大小。依旧以上述A、B设备为例,展示结果如下:
左边是A设备,右边是B设备。
明显地看出,在A设备显示比B设备大很多,实际上和我们之前用px来描述view的大小原理是一样的,bitmap的宽、高都是px在描述,而bitmap决定了view的宽、高,最终导致A设备和B设备上的view大小(宽、高像素)是一样的,而它们屏幕密度又不相同,因此产生了差异。
那不会每次都需要我们自己根据屏幕密度来转换bitmap大小吧?幸运的是,Android已经为我们考虑到了。
生成不同密度的目录有什么作用?
A设备dpi=240,根据dpi范围,属于hdpi
B设备dpi=420,根据dpi范围,属于xxhdpi
图片原始尺寸:photo1.jpg(宽高 172px-172px)
当我们想要在不同密度设备上显示同一张图片并且想要“看起来一样大时”。假设设计的时候以hdpi为准,放置photo1.jpg为172*172,那么根据计算规则在xxhdpi上需要设置photo1.jpg为:
现在hdpi和xxhdpi目录下分别存放了同名图片:photo1.jpg,只是大小不同。当程序运行的时候:
来看看效果:
左边A设备,右边B设备
针对不同的密度设计不同的图片大小,最大限度保证了同一图片在不同密度设备上表现“看起来差不多大”。
来看看A、B设备上图片占内存大小:
说明在B设备上显示photo1.jpg需要更多的内存。
上边只是列举了hdpi、xxhdipi,同理对于mdpi、xhdpi、xxxhdpi根据规则放入相应大小的图片,程序会根据不同的设备密度从对应的mipmap文件夹下加载资源。如此一来,我们无需关注bitmap在不同密度设备上显示问题了。
在mipmap各个文件夹下都放置同一套资源的不同尺寸文件似乎有点太占apk大小,能否只放某个密度下图片,其余的靠系统自己适配呢?
现在只保留hdpi下的photo1.jpg图片,看看在A、B设备上运行情况如何:
看起来和上张图差不多,说明系统会帮我们适配B设备上的图片。
再来看看A、B设备上图片占内存大小:
先看A设备:
对比photo1.jpg 分别放在hdpi、xxhdpi和只放在hdpi下可以看出:B设备上图片所占内存变小了。为什么呢?接下来从源码里寻找答案。
A、B设备同样加载hdpi/photo1.jpg,返回的bitmap大小不相同,我们从这方法开始一探究竟。
上面涉及到的关键点是density,分别是TypedValue的density和Options的density。
先来看看TypedValue density:
再来看看Options density
现在分析B设备加载hdpi/photo1.jpg如何做的:
和我们之前调试的结果一致。
B设备是怎么决定使用hdpi下的图片资源呢?
根据实验(尝试找了源码,没怎么看懂,因此只是做了实验,可能在不同密度设备上找寻规则不一样):B设备先找属于自己密度范围文件夹下的图片,B设备属于xxhdpi,先查看xxhdpi有没有photo1.jpg,如果没有则往更高的密度找,比它高的密度是xxxhdpi,还是没有,则往低密度找,找xhdpi,没有再找hdpi,找到了则返回构造好的TypedValue,剩下的就是我们前面分析的。
既然我们只想放某个密度下的一份切图,该放哪个密度下呢?从系统寻找规则看,更推荐放置在更高密度下的,因为如果放在低密度下,那么当运行在高密度设备上时,图片会进行放大,可能导致不清晰。我一般习惯放在xxhdpi下。
Android Studio默认创建了不同密度的mipmap文件夹,默认放置了ic_launcher.png。我们普通的切图该放drawable还是mipmap下呢?对于这个问题网上也是众说纷纭,实际上对于我们来说,关注的重点是图片放在drawable或者mipmap,加载出来bitmap是否有差异,如果没有差异放在哪就看习惯了。通过实践,普通的切图放drawable和mipmap下加载出来的bitmap是没有差异的,只不过用drawable的话需要自己创建不同密度的文件夹。我习惯于放在drawable下(启动图标logo还是放在mipmap下)。
前边 [注1] 留了个问题,我们使用dp来表示view的大小了,为啥两个看起来还是有些差距?下面我们更加直观地看一个例子。
A设备dpi=240 密度1.5 分辨率(宽高px):480 * 800
B设备dpi=420 密度2.625 分辨率(宽高px):1080 * 1794
换算成dp
A设备分辨率:320dp * 533dp
B设备分辨率:411dp * 683dp
依旧是上边的例子:
将view宽高分别设置为320dp,看看效果:
左边A设备,右边B设备
可以看出同样的320dp大小,A设备铺满了屏幕,而B设备没有。这效果显然是不能接受的,Android考虑到不同设备宽高不同,推出了"宽高限定符"。以A、B设备为例:
在res文件夹下创建文件夹:
假设设计师出图是按照800x480,那么我们创建dimen文件的时候
该文件放在values-800x480文件夹下。
根据分辨率比例算出1794x1080的dimen值
这样子,A、B设备加载资源的时候使用对应分辨率限定符下的px,如果找不到再找默认值,可以在一定程度上解决屏幕宽高碎片化适配问题。
但是这样子的限定比较严格,需要测试各种分辨率,后来Android又推出了"smallest-width"简称最小宽度限制。
A设备宽320dp
B设备宽411dp
假设设计师切图标准屏幕宽是320dp(A设备),那么可以定义如下dimen.xml文件
该文件放在values-sw320dp文件夹下
根据规则,计算B设备dimen.xml
现在我们继续来看之前的view
通过对dimen引用,A设备寻找和自己宽度一样的dimen文件,找到values-sw320dp,dp320=320dp。B设备寻找和自己宽度一样的dimen文件,找到values-sw411dp,dp320=410dp。这样子同样的dp320,得出不同的值,就适配了屏幕宽度不同的问题。
看看效果:
这次B设备也铺满了屏宽。
综上,为了适配不同屏幕大小,推荐使用dp+smallest-width。
获取设备dpi最终都是从这方法获取的,实际上就是读取系统的配置文件。因此我们也可以通过adb shell 获取:
可以看出dpi是系统配置好的,当然有些手机是可以设置分辨率的,设置之后我们查看分辨率:
分辨率变低了,dpi也变小了。