android 尺寸和适配
android 尺寸和适配
神手-追魂 发表于5个月前
android 尺寸和适配
  • 发表于 5个月前
  • 阅读 6
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 新注册用户 域名抢购1元起>>>   

写过多少布局,做过多少适配。我认真着,你的不知所措。这种迷茫心情 我想谁都会有,幸运的是能分担你的愁。我tm居然唱出来了。你敢信?

没错,今天鸡排君从头讲设备尺寸和适配这点事情。也许当时跑得太急,没好好回头欣赏它的美。学习的时候只看到了怎么用,却没有思考这些是怎么出现的。现在握紧我的手,带你一步一步推导这些看上去很基础,却有时模模糊糊的东西。(比如自定义View里的单位如何适配?)

本篇你能收获什么?

  • 如标题一样,彻底搞清楚android上的适配由来而不是背了概念

  • 还有抓住一直大鸡排。

目录

  • 什么是屏幕尺寸

  • 一寸到底是多少?

  • DPI每英寸点数

  • 独立的像素密度是什么?

  • 屏幕相关的api

注:这里的推导是自己的理解,非官方的解释

什么是屏幕尺寸?

我们来看下面这幅图,屏幕对角线的长度。(即:设备的左上角至右边下角那条线)

注意:我们需要知道对角线的长度怎么算的,其实很简单,利用勾股定律即可。

根号下720平方+1280平方等于设备对角线像素。

√720*720+1280*1280
=√518400+1638400
=√2156800
≈1468.6047800(px)

另一个疑惑就出现了,我们平时描述的4.3寸、5寸是指屏幕的什么。
这里的寸是指什么鬼?
为什么我们算出了是1468这么大?
黑人问号脸??why

一寸到底是多少?

那哦跟浓港啊,介锅一寸 巴拉巴拉啦.....
停 打住,我听不懂那些。

那好吧,我们现在手上这台设备是720*1280,我们前面计算过它的屏幕对角尺寸约等于1468个像素密度。注意这里说的是屏幕对角尺寸不是屏幕尺寸。商家描述说这台碉堡的手机是4.5寸的。既然这里的尺寸就是指对角线。我们反向推算。

1468/4.5
≈326.2222

这里得到326.222就是我们这台设备的一寸所占的像素点。

DPI每英寸点数

我们首先知道,谷歌官方把android设备的参考标准定义为一寸是160px 
这里会get一个概念叫dpi(dpi:dots per inch )。即每寸的像素有多少个点。面试的时候经常有同学把dpi、dip、dp弄混了。下面我接着推。

现在要说一下独立的像素密度了。独立的像素密度??听着就好绕口,俺有一句mmp,不知道当讲不当讲,这又是蛇?

独立像素密度DIP/DP

好,既然谷歌的一寸是160px,为什么我们是刚刚算出来的是320px?(为了方便计算,省去那6个像素点,别跟我纠结了)

嗯,这个问题问得好!

因为android设备中160px每寸的密度无法满足用户的钛合金眼,甚至觉得颗粒感爆炸好喵?
所以厂商将 原本160个像素中又插入一波160像素。就变成 了320像素。如下图所示。(真实的屏幕生产不是这样插像素的,这里只是便于理解。)


一开始只有红色的像素点,后面插入了一些像素进来。在同样的空间下密度增大了一倍。所以320像素得到了解释。

由此我们又得到一个概念叫密度。(density)

320/160
=2

这里的密度就是2了,所以我们开发中写1dp在720*1280的设备上会变成2px。有兴趣的小伙伴可以写一个自定义View把1dp设置成宽度,然后在View里打印一下1dp的宽度得到的是不是2px。同理在其他分辨率上也是一样的算法。
如:240/160=1.5(倍密度)

实际情况下,你现在的设备如果密度是2.0,那么你布局时1dp将转换成2px在设备上呈现。
到这里我们总算是明白了,什么是独立像素密度。即:dp转换成px代表几个像素。业界把它叫做 Density independent pixels 简称dip,直译过来的意思是独立的像素密度。

用我自己的话来讲,*dp是运行在物理设备之前用来描述控件大小的一种描述单位,其中我们开发中用的单位dp就是dip一个意思。而dpi是指每寸像素密度。不要再弄混了喔。喔喔喔!!!

现在回过头来看下面的概念,是不是清晰多了?

  • dip: Density independent pixels ,设备无关像素。

  • dp :就是dip(以后别纠结dip和dp的区别了好么)

  • px : 像素

  • dpi :dots per inch ,一英寸多少个像素点。素密度

  • density : 密度

  • 分辨率 : 横纵2个方向的像素点的数量

  • 屏幕尺寸: 屏幕对角线的长度

屏幕相关的api

  DisplayMetrics metric =Resources.getSystem().getDisplayMetrics();
        int DMwidth = metric.widthPixels;  // 屏幕宽度(px)
        int DMheight = metric.heightPixels;  // 屏幕高度(px)
        float DMdensity = metric.density;  // 屏幕密度(/ 1.0 / 1.5/ 2.0)
        int DMdensityDpi = metric.densityDpi;  // 屏幕密度DPI(160 / 240/ 320)
  Log.e("metric","屏幕宽度="+DMwidth+" 屏幕高度="+DMheight+" 屏幕密度="+DMdensity+" 屏幕密度DPI"+DMdensityDpi);
      

在自定义View上做适配,多数情况下可以根据权重和获取整个控件的宽高然后根据百分比去设置某个特殊的属性需要用到的值。but,这些都太常见了。也不是一个我们今天要讲的重点。

不知道大家有没有纠结一个问题,在自定义View里,经常要定义Paint用来绘制。但是setStrokeWidth(float width)里的入参接受的到底是以pxdp? 哪个为单位。这里有本质上的区别。比如你写了一个控件,自己的开发机器没问题,结果换一个设备就尺寸就不准了。

我先卖个关子,不告诉你答案╭(╯^╰)╮。为此我们用官方TextVIew源码和TypedArray源码作为分析。

这是一条温柔的提示:来么,宝贝儿!打开我们的AndroidStudio快速轻击Shift两下,然后输入TextView,我们一起看流星吧。(里面真的有很多小星星都是注释)

目录

  • TextView中的textSize源码分析

  • TypedArray源码分析

TextView中的textSize源码分析

textSize默认大小

我们可以看到默认情况下,TextViewtextSize是15。

重绘方法

而后我发现了这样一个方法,用于重新设置FontSize,这里逻辑很清晰,判断Paint的Size和刚刚重设是否一致,一致就重设Paint的参数,并重新绘制View。

最后调用的是Native方法,java层已经看不到了。

setTextSize发现TypedValue

不过这些不是重点,我们还发现了另外一个方法。TextVIew设置字体有重载方法。我们重点去看两个参数的。

这里说明了Size的单位大小是由unit来决定的,最后它调用了SetRawTextSize方法,而这个方法我们前面分析了是用来重绘的。我们来看Unit是个什么值。

通过TypedValue类发现实际所有的单位都是定义在这里的。而我们的setTextSize需要的单位也在这里。

TypedValue.applyDimension

重头戏来了,如果我们用了两个参数构造方法进行设置字体大小,那么。
TypedValue.applyDimension( unit, size, r.getDisplayMetrics())做了什
么,他是如何转换单位的。我跟进去看。

我们来看最典型的一条case语句 case COMPLEX_UNIT_DIP: return value * metrics.density;
这里的density就是我们上个章节里讲到的密度,也就是说如果你用dp作为单位,他就已当前的size乘以密度返回。
好家伙,恍然大悟把?实际上这里把你传入的任意单位最后都转换成了像素来处理的,最后设置给了Paint

那么这里就说明了一个问题,Paint对象接受的参数最后都是px像素来使用的,不要漏掉了这一点喔。

这里我推荐直接使用TypedValue.applyDimension()来处理。这是android已经具备的Api,不需要自己再从零写了。可能有小伙伴用过类似dp2Px()这种类库。我个人不是很推荐往项目里堆叠繁杂的库进来,一来是会变得越来越臃肿,而是如果单纯的依赖不方便去修改源码,出了问题,你要么选择copy出代码出来进行修改,要么反射一下。但这都不是很优雅。尽可能使用系统已有的。

参考代码:

 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, Resources.getSystem().getDisplayMetrics());

TypedArray源码分析

什么是TypedArray

如果有常写自定义View属性的小伙伴一定对TypedArray不陌生,我们常用来在Java文件中取自定义View上的属性。这些属性可以是布尔、dp、整型、字符串甚至是一个布局文件的id等。

现在我们思考一个问题,平时在一个控件里描述一个长度用了dp为单位,他是如何适配在不同设备上的?

带着这个疑惑,我们来看源码如何解析的。首先我们不管是原生控件也好,还是自定义控件也好,我们都需要在构造方法中去取出XML布局文件中的属性。

获取TypedArray

这里获取TypedArray对象,其中R.styleable.xxx是该控件拥有的属性。

TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.xxx);  

接着我们来看一眼TypedArray都有那些方法。

TypedArray的使用

实际上,我们的控件从XML中去解析就是通过该TypedArray的API来操作的。见TextView获取textSize源码如下。


我们来看一眼,getDimensionPixelSize方法内部的逻辑。

注意关键的一行`TypedValue.complexToDimensionPixelSize(

            data[index+AssetManager.STYLE_DATA], mMetrics);`, 我们跟进去看。


是不是似曾相识?熟悉吧,没错,该方法最终还是调用了TypedValue.applyDimension()进行了单位换算。我们上一章节讲过这个。我就不再展开里面的逻辑了。到这里真相大白。实际上在xml写的dp、pt、px等,最终都要来这里一趟,将其转换为px。再画出来。所以又一次证明了,我们的画图操作都是基于px为单位的。

流程总结

  • 1.xml定义一个属性

  • 2.在构造方法里用TypedArray来get这个属性。

  • 3.调用getDimensionPixelSize()方法获取该属性上的具体值,并转换单位为像素。

  • 4.绘制出来

我们发现它并不复杂,但很多时候往往是没有去阅读过源码,在工作中不能流畅的解决这些问题。
有小伙伴反应说他的自定义View通过java代码设置尺寸就是不能适配,但是通过属性就可以。这里其实就是忘记调用TypedValue.applyDimension( unit, size, r.getDisplayMetrics())方法。
再比如面试过程中再有面试官问如何做适配的时候,你就不会再是背诵面试题上的答案了吧?

题外话,我反对那些面试宝典类的东西,不是说这些知识的对错性,而是很多新人在学习的过程中往往是囫囵吞枣,出发的目的如果是为了面试通过背诵这些东西,而不理解其中的概念,甚至想都没有想过为什么是这样。这样的结果绝对不是面试官想看到的。也不是我们应该做的。我们慢一点不要紧,多花点时间,不能途一时之快,那些偷过的懒,都是要还的。

共有 人打赏支持
粉丝 0
博文 14
码字总数 360
×
神手-追魂
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: