理一理屏幕尺寸那些事

注:本文的目的在于理清楚一些尺寸关系,如果有表述不当,欢迎指出讨论


一、科普常识:

0.测试准备

手上有两个真机: oppoA77(1920*1080 5.5英寸)、 oppoR15X(2340*1080 6.4英寸) 、 再加一台模拟器(480*320 3.5英寸)仿OPPO R801 辅助:一台笔记本电脑联想Y480N(768*1366 14英寸) 和一个iPad_Air_2(2048*1536 9.7英寸)

测试资源.png


1.按照像素来算引出的问题(反证法):

如果,我说如果一个像素代表n个物理毫米,当n等于1时 那么oppoR15X(2340*1080)相当于23.4*10.8cm,量了一下,大概跟书差不多 OPPO R801(480*320)相当于(4.8*3.2cm),量了一下,差不多跟橡皮一样大

像素级截图比较.jpg

在同一参考系下,玩oppoR15X和OPPO R801,相当于玩一本书和玩橡皮的区别 显然我并没有这样的体验,这只能说明,对于两个不同的手机,它们的n值不同 也就是两个手机:1个物理毫米中所含的像素个数是不同的


2.手机英寸的概念

英寸是衡量手机屏幕的真实大小 我们买手机一般关心的是手机是多少多少英寸的,然后懂行的看看分辨率,那英寸代表什么? 1英寸 = 2.54 厘米: 大概和一元硬币的直径差不多,不信你拿六个硬币排在对角线比一下

英寸.png

由于分辨率确定了长宽比,我们不难算出oppoR15X和OPPO R801的物理宽高

\

宽px

高px

对角线in

对角线cm

物理宽cm

物理高cm

oppoR15X

1080px

2340px

6.4in

16.256cm

6.81cm

14.76cm

OPPO R801

320px

480px

3.5in

8.89cm

4.93cm

7.40cm


3.屏幕属性的封装

这么多属性,一个一个算也怪累人的,计算器点着也不爽,封装一下吧ScreenInfo.java

/**
 * 作者:张风捷特烈<br/>
 * 时间:2018/12/1 0001:8:01<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:屏幕尺寸信息
 */
public class ScreenInfo {
    public static final float INCH_TO_MM = 25.399999961392f;//英寸转为毫米数
    public String name;//设备名称
    public float inchC;//英寸
    public int pxW;//屏幕宽像素数
    public int pxH;//屏幕高像素数

    public int pxC;//对角线像素数
    public float ppi;
    public float dpi;
    public float relW;//实际宽度
    public float relH;//实际高度
    public float relC;//实际对角线长度

    public ScreenInfo() {
    }
    public ScreenInfo(String name, float inchC, int pxH, int pxW) {
        this.name = name;
        this.inchC = inchC;
        this.pxW = pxW;
        this.pxH = pxH;
        pxC();
        ppi();
        dpi();
        relC();
        relW();
        relH();
    }
    private int pxC() {
        pxC = (int) Math.sqrt(pxH * pxH + pxW * pxW);
        return pxC;
    }
    private float ppi() {
        ppi = pxC() / inchC;
        return ppi;
    }
    private float dpi() {
        dpi = pxC() / inchC;
        return dpi;
    }
    private float relW() {
        relW = relC * (pxW * 1.f / pxC());
        return relW;
    }
    private float relH() {
        relH = relC * (pxH * 1.f / pxC());
        return relH;
    }
    private float relC() {
        relC = inchC * INCH_TO_MM;
        return relC;
    }
    @Override
    public String toString() {
        return "ScreenInfo{" +
                "\nname='" + name + '\'' +
                "\n, 屏幕尺寸/英寸=" + inchC +
                "\n, 屏幕横向像素数=" + pxW +
                "\n, 屏幕纵向像素数=" + pxH +
                "\n, 屏幕对角线像素数=" + pxC +
                "\n, ppi=" + ppi +
                "\n, dpi=" + dpi +
                "\n, 屏幕对角线物理尺寸/mm=" + relC +
                "\n, 屏幕横向物理尺寸/mm=" + relW +
                "\n, 屏幕纵向物理尺寸/mm=" + relH +
                '}';
    }
}

尺寸解析.png

好吧,知道大家不喜欢看数据,于是我自定义了一个View,用ScreenInfo信息画个图示,感觉还蛮好的 三行代码就能画一个手机信息图,也不是很难,有兴趣的可以看看源码,或自己画画 我是按照物理尺寸画的,所以现实中它们的屏幕相对大小就是这样的!

屏幕尺寸.png

如果你想玩,其他的屏幕也可以试试:只要知道分辨率和多少英寸

查看其他屏幕尺寸.png


4.密度:
什么是密度?----紧密程度?

上学的时候应该听过线密度,面密度和体密度、或者人口密度吧。
比如一个市的人口密度:合肥市面积为11445.1平方公里,人口为779万,人口密度为680.6人/平方公里
也就是合肥市平均 1平方公里 有680.6人
现在把屏幕当做土地,把像素点当做人,这些人一个一个有秩序地排着 
那么OPPOR15X中1mm的长度排多少个像素,显而易见:2577px/163mm = 15.809...  取个整 15.8
那么 1 平方毫米能容下几个像素呢? 15.8*15.8 = 249.64个/mm^2  约250个/mm^2

那么OPPOR801中1mm的长度排多少个像素,显而易见:2577px/163mm =  6.471...  取个整 6.5
那么 1 平方毫米能容下几个像素呢? 6.5*6.5 = 42.25个/mm^2  约42个/mm^2

相当于在一片等大的土地上,一块占了250个人,一块占了42个人,神奇的是两边都把这块地占满了
于是真相(得出的结论)只有一个:两块土地上一块是小人,一块是巨人  

现在把物理尺寸:1mm的屏幕放大

1px.png

记得小时候的手机肉眼就能看到一点一点的像素,就是因为1mm里的像素点少,相对而言一粒像素就大 现在的手机可是瞅不出来像素了,那用光学显微镜看一下OPPO-R15X吧


5.ppi与dip

现在我们手上的信息还蛮多的,这些信息有什么用?

ppi(Pixel Per Inch),即每英寸的像素。
我们刚才好像算了每毫米的像素数,那每英寸的像素数能难倒你吗?
OPPO-R15X 的 ppi : 2577px/6.4in = 402.65625 px/in   约402.6ppi
OPPO-A77 的 ppi : 2202px/5.5in = 400.363... px/in   约400.4ppi
OPPO-R801 的 ppi : 576px/3.5in = 164.571... px/in   约164.6ppi
ppi形象一点的比喻:

一个一元硬币直径约1 in,现在让一元硬币(包括背景)等大显示在三个手机上: OPPO-R15X需要用:402*402=161604 个像素点 OPPO-A77需要用:400*400=160000 个像素点 OPPO-R15X需要用:164*164=26896 个像素点 我们知道像素组成了显示的图片,也就是说用161604个点和26896个点组成相同的画面 那么26896的那个看起来效果自然要比161604的差很多,161604更加紧密,所以视觉感好

ppi什么意思.png

来分析一下笔记本电脑:

ppi=1567/14=111.928...,也就是 1 in 里有112个点,1 in = 25.4mm 那说明一个像素的大小是 25.4 / 112 = 0.226mm ,人眼可视长度是0.1mm,所以你近些看可以看到颗粒

笔记本屏幕分析.png


dpi(Dot Per Inch),即每英寸的点数。
dpi又是什么鬼,点数又是什么鬼?---dpi称为打印精度

打印机将[彩色液体油墨]经喷嘴变成细小微粒喷到印纸上,一个颗粒代表1点  
dpi的意思是每英寸墨滴点数,比如300dpi的意思就是每英寸墨滴的个数为300
用300个点表示一个硬币,和72个点表示一个硬币,可想而知300的更加精细

大学时做要打印的ps产品效果图都要把图片的dpi调到300以上,因为大幅的海报需要细致的像素表达
普通的web图片只要求72dpi就够了,因为只是显示在屏幕上而言 
ppi和dpi在Android
Android又不是打印机,dpi和ppi等价,都是表示 1 in长度对应的px数  
也许谷歌更倾向于用`点(dot)` 来表述屏幕像素,所以采用dpi的说法而不是ppi

二、谁动了我的图片尺寸?

测试图片.png

1.我的250*200的图片画出来怎么尺寸不对?----Q1

实验图片:250*200,300dpi,res\mipmap-xhdpi\wy_250px_200px_300dpi.jpg

mipmap_xhdpi_250px_200px_300dpi.png

实验图片:250*200,72dpi,res\mipmap-xhdpi\wy_250px_200px_72dpi.jpg

mipmap_xhdpi_250px_200px_72dpi.png

这是挺纠结的一个问题,我预想的是在小手机上图片250px应该会很大 为什么并不是我所预料的那样?而且自定义的图片dpi被无视了?----Q2


2.我怀着满心的疑问将图片拷贝到drawable里

实验图片:250*200,72dpi,res\drawable\wy_250px_200px_72dpi.jpg

drawable_250px_200px_72dpi.png

实验图片:250*200,300dpi,res\drawable\wy_250px_200px_300dpi.jpg

drawable_250px_200px_300dpi.png

貌似对dpi还是免疫,而且OPPO-R15X图片按比例放大了,分别弹了一个metric.density,一个3,一个1 哥就想都显示250*200为什么这么难?----Q3


3.怀着疑问,分别将图片在mipmap各个文件夹放一遍

测试结果如下,并画了一张高度变动的简单示意图

高度分析.png

可以看出:
1.OPPO-R15X在xxh的时候显示原图,OPPO-R801在m的时候显示原图
2.每种情况下OPPO-R15X的高度总是OPPO-R801的3倍
结论:OPPO-R15X自身dpi(ppi)为402,被圈入了xxh的领域,OPPO-R801自身dpi(ppi)为164,被圈入了m的领域
xxh对应的dpi/m对应的dpi = 3

数据.png

Q1:谁动了我的图片尺寸
---mipmap的不同文件夹,Android会区分对待

Q2:而且自定义的图片dpi被无视了?
----图片自身的dpi对屏幕设备的显示并没有效果,只对打印有影响

Q3:哥就想都显示250*200为什么这么难?
---- 难! 但是也没有这个必要,你非怎么想,在所有的mipmap文件夹都放一张图,
然后所有手机会显示250*200,不过有人打你不关我事...  

4.获取不在mipmap里的图片会怎么样?

这个问题问的好,代码测试走一波

不出所料,从文件读取的图片,没走mipmap,所以原像素显示

load_form_file.png

总结:mipmap会根据图片的文件夹位置对图片在不同density设备上进行不同的缩放,也就是"自动适配"


三、看看那些尺寸

1.dp的困境:

想必大家这个方法都用过

protected float dp(float dp) {
    return TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}

来看看它到底干了什么:applyDimension

public static float applyDimension(int unit, float value,DisplayMetrics metrics){
    switch (unit) {
    case COMPLEX_UNIT_PX:
        return value;
    case COMPLEX_UNIT_DIP:
        return value * metrics.density;//将值*density返回
    case COMPLEX_UNIT_SP:
        return value * metrics.scaledDensity;
    case COMPLEX_UNIT_PT:
        return value * metrics.xdpi * (1.0f/72);
    case COMPLEX_UNIT_IN:
        return value * metrics.xdpi;
    case COMPLEX_UNIT_MM:
        return value * metrics.xdpi * (1.0f/25.4f);
    }
    return 0;
}

metrics.png

代码追踪:
--> getResources().getDisplayMetrics()

-->[android.content.res.Resources#getDisplayMetrics]
public DisplayMetrics getDisplayMetrics() {
    return mResourcesImpl.getDisplayMetrics();
}

-->[android.content.res.ResourcesImpl#getDisplayMetrics]
DisplayMetrics getDisplayMetrics() {
    if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
            + "x" + mMetrics.heightPixels + " " + mMetrics.density);
    return mMetrics;
}

-->[现在要看: mMetrics.density被赋值时]
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
    mMetrics.densityDpi = mConfiguration.densityDpi;
    mMetrics.density =
            mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
}

搜索到:mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE;

mMetrics.density.png

我们被奉为灵丹妙药的dp只不过获取了DisplayMetrics.DENSITY_DEVICE,再乘以0.00625(即除以160) 而且这个DisplayMetrics.DENSITY_DEVICE也并非屏幕真正的dpi(ppi),400的,402的都算是480, 所以dp的计算方式也是一个满足大众需要的约值,虽然有一定的效果,但并不能完美适配。


真的有完美的适配吗?

除非你能让长宽是100*180的纸片能够恰好装满长宽是100*200盒子而且没有变形 或者让所有的安卓手机厂家生产相同比例的手机,否则无论怎么配会有瑕疵, 鱼和熊掌不可兼得 ,但舍生和取义之间还有平常地活着(尽管并不完美)

我曾经有想过,为什么手机不是圆形的,如果是圆形的就不需要适配了,永远等比例  
也许兜里放着占地方吧,或者看电影,可用面积会比较少...感觉好可惜

文字单位:sp
-->[android.util.TypedValue#applyDimension]
 case COMPLEX_UNIT_SP:
     return value * metrics.scaledDensity;

-->[android.content.res.ResourcesImpl#updateConfiguration]
 mMetrics.scaledDensity = mMetrics.density *
    (mConfiguration.fontScale != 0 ? mConfiguration.fontScale : 1.0f);
    
//现在焦点移到:mConfiguration.fontScale != 0
//如果是假的,那就和mMetrics.density的值相同,否则是mConfiguration.fontScale

其中configuration.fontScale是根据系统字号改变的,默认是1,所以会遇到dp和sp混用无影响的情况。但,一旦用户改变了系统字号,有一定的缩放量,dp的为sp就原形毕露了,所以字体还是乖乖用sp,别没事找事。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程坑太多

『高级篇』docker之Mesos集群架构图(23)

11640
来自专栏GreenLeaves

TFS2018环境搭建一硬件要求

TFS可以安装在Windows Server和Windows PC操作系统中,但是TFS2018和2018只支持64位操作系统中,早期的版本没有操作系统的位数限...

40730
来自专栏机器之心

Diss所有深度生成模型,DeepMind说它们真的不知道到底不知道什么

深度学习在应用层面获得了巨大成功,这些实际应用一般都希望利用判别模型构建条件分布 p(y|x),其中 y 是标签、x 是特征。但这些判别模型无法处理从其他分布中...

11510
来自专栏smy

一张图解释负载均衡

首先当大量用户访问时候,先请求到nignx服务器,因为nignx对于高并发支持较好,所以由nignx服务器将访问需求分配给不同的apache服务器,apache...

20630
来自专栏数据结构笔记

python基础类型(一):字符串和列表

注意到最后三个的单双引号是嵌套使用的,但是最后一个的使用方法是错误的,因为当我们混合使用两种引号时必须有一种用来划分字符串的边界,即在两边的引号不能出现在字符串...

13820
来自专栏Python专栏

200行代码,一行行教你自制微信机器人

1) 用一个windows客户端工具运营公众号,真的很局限。虽然工具的功能很强大,能自动添加好友,自动拉好友入群,关键字回复等等,但是有一个绕不开的点,它是一款...

61220
来自专栏大数据文摘

迷人又诡异的辛普森悖论:同一个数据集是如何证明两个完全相反的观点的?

在辛普森悖论中,餐馆可以同时比竞争对手更好或更差,锻炼可以降低和增加疾病的风险,同样的数据集能够用于证明两个完全相反的论点。

15130
来自专栏苦逼的码农

一些常用的算法技巧总结

数组的下标是一个隐含的很有用的数组,特别是在统计一些数字,或者判断一些整型数是否出现过的时候。例如,给你一串字母,让你判断这些字母出现的次数时,我们就可以把这些...

21230
来自专栏我是攻城师

理解BitMap算法的原理

位图:一种常用的数据结构,代表了有限域中的稠集(dense set),每一个元素至少出现一次,没有其他的数据和元素相关联。在索引,数据压缩,海量数据处理等方面有...

24430
来自专栏chenssy

多线程:为什么在while循环中加入System.out.println,线程可以停止

这个我们都知道,由于 stopReqested 的更新值在主内存中,而线程栈中的值不是最新的,所以会一直循环,线程并不能停止。加上 Volatile 关键字后,...

21440

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励