上一篇文章发到掘金后,发现省略了内容后,居然不少人说看不懂;这就尴尬了。我本来以为DP这个东西提及来实在布同样长,并且是入门的东西,不用在讨论。但发现不少人对DP适配存在误区,尤为还有人任务不一样设备屏幕像素不同须要适配的问题。因此这里说一下DP究竟怎么来的。android
先直接看Wiki 像素。简单说,像素就是表示一个点的RGB颜色;这个点,是数学上的概念,是没有大小的(回顾一下z初中数学,我没记错吧)。就是说,咱们要描述一幅图像(好比清明上河图),能够转换成X*Y像素的位图(Bitmap),但转换后咱们是不知道这个Bitmap原来的物理尺寸,就这样丢失了。为何会这样?由于现实世界是连续的,不可能记录下来(有限的存储空间放不下无限量的数据)。Bitmap也是没有物理大小的。bash
把Bitmap从新表示出来,咱们接触到最多的就是屏幕和打印了。要展现像素,最简单的就是在屏幕上取一个点对应一个像素的点就好了。假如把屏幕分为1080*1920个点,这样一样1080*1920像素的Bitmap恰好展现。屏幕上的点虽然对应像素的点,但它是有物理尺寸的(经过屏幕宽高除以份数获得)。知道了展现规则,虽然像素自己是没有大小的,但咱们也能够用像素来表示一个图形展现时究竟有多大。好比一个字体是16px,这不是说它的物理尺寸有多大,而是它显示时会占据多少个点。网络
屏幕点和像素对应,很长时间内都是这样过来的,直到后来手机从奢侈品变成日用品再变成消耗品。手机的普及,每一个人成天都抱着手机屏幕盯着看,而后发现了问题:屏幕颗粒看起来太大。屏幕上每英寸上点的数量,咱们叫作DPI(dots per inch,由于屏幕上面每一个点对应一个像素,因此也有叫PPI;二者大部分状况混用,有时候又不同,能够参考,不展开讨论)。当DPI小的时候,每一个点的物理尺寸就变大(点的大小理解为 DPI 分之一 英寸;好比DPI是160,每一个点就是 1/160 英寸);因此要解决屏幕颗粒大的问题,提升DPI值就能够了。但DPI提升后,又出现了另一个问题:一样像素的Bitmap在新的屏幕上看起来小了。假设把DPI从160提升到320,原来160像素在新屏幕上就只覆盖了1/4英寸。ide
无规矩不成方圆。要完整的解决问题,须要订立新的标准,不能再让屏幕点和像素一一对应了。这里是屏幕展现大小,为何咱们不直接以尺寸为标准呢?简单粗暴直接规定一英寸就是160dp,若是是160dpi的设备,1dp对应一个点(像素);若是是320dpi的设备,1dp对应两个点(像素)。这样咱们要描述一个控件究竟多大,原来用像素的地方,就改为DP。好比一个用户头像能够是48dp,表示它的大小是48/160英寸。注意,这里DP只是取代了像素做为描述控件展现大小的做用,实际展现时屏幕上仍是点,系统内部Bitmap用的仍是Px。使用DP的好处是把控件大小转换为物理上的尺寸,让不一样dpi上的控件能够看起来大小同样。
这个DP固然也能够从其余方面理解,原来用px表示大小(一个px对应一个点),如今屏幕dpi提升了一倍,以前一个点的大小如今就要对应4个点。这样用px表示大小就不合适了,得从新取个名字(dp),固然也有叫pt的(iOS大小单位)。无论这个单位叫什么,它表示的都是一个物理尺寸的单位,把物理尺寸大小和和最终展现点的数量进行了剥离。
用DP做为描述大小的单位后,Bitmap展现的问题尚未解决。在160dpi上铺满一平方英寸须要160*160像素,但320dpi上须要320*320像素(依然使用160*160只能铺满1/4,前面提到的问题)。这个问题如今解决起来也简单,咱们把160做为标准(mdpi),把320做为两倍图(xhdpi),为每一个标准建立一个文件夹,一样的图片在mdpi里面放标准的,在xhdpi放mdpi 2倍大的。再扩展一下,还能够支持0.75倍的,1.5倍的,3倍的,4倍的。 到这里,咱们用dp解决了大小单位的表示问题,还兼容了原来的Bitmap展现。DP是Android方案,实际上iOS用的pt也是一样的思路,mmdpi、xhdpi也对应iOS的1倍图、2倍图。须要注意的是,上面说1英寸为160dp,不是强制的,而是灵活的;硬件厂商能够是必定范围为调整,但总的1dp的视觉大小并不会差距太大;这也是DP的出发点,让一样单位(dp)的控件再不一样设备上看起来同样大。
就这样,新的单位订好了,也解决了和像素直接的转换问题。工具
引入了DP,这是对开发而已的。对于设计师来讲,他面对的仍然是px。对于手机App的设计而已,设计师会取720p、1080p或者750*1334做为原型大小开发。对应到市面上的手机,咱们能够直接任务720p、750p是xhdpi的,1080是xxxhdpi的。一个控件的dp单位的大小用它px的大小除以2或者3就能够获得;如今有原型工具也支持这种换算。这能够理解成是一个约定好的尺寸,或者一个实践得出的结果。实际上,设计师也是遵循设计指导的,好比Material Design(虽然官方建议margin padding用8的倍数,但不少设计师用各类奇怪的大小)。布局
屏幕适配是任何UI设计须要面对的问题,从移动设备出来以前,PC软件和Web就积累了屏幕适配的方案了。咱们先说屏幕适配的来源,再说以前的经验,最后说点实践。
引入了DP概念后,对开发而已,屏幕的大小就是以dp来看的。Android是开放系统,设备众多,比较通用的能够分为两类:手机和平板。这些设备的都是ldpi-xxxxhdpi的(实际上Google本身还弄出了420dpi和560dpi的设备);由于如今UI设计上通常认为垂直方向是能够无限延长的(上下滚动),高是必定知足展现的,更多的咱们要注意不一样宽度的屏幕适配。以手机为例,通常宽度介于320到411dp之间。这就是说开发是适配手机(不包括平板),布局必须能适应320到411dp之间的任何宽度,这就是开发要面对的适配问题。
在PC软件和Web上,它们不只要适配不一样屏幕的像素,还要处理同个屏幕上父窗口的不一样大小;这个在核心上是和移动适配是一致的,可是要不一样尺寸的外部约束下,很好的处理内部的控件位置。这个适配处理,其实是个设计问题。主要的思路是,设计时以屏幕上下左右做为锚点摆放控件,规定好控件的margin和padding,宽高会变化的控件自适应(TextView,各类父布局),宽高固定的控件写死大小(头像控件等)。开发者还原出设计师的设计思路,就能作出知足屏幕适配的布局。这里我强调还原设计思路,而不是还原设计。设计图不能展现在不一样尺寸上的效果,但设计思路已经设计到了。仍是上一篇文章的例子,顶部的tab设计图上是308dp,设计思路是要表示它距离左右两侧22dp(这样左侧恰好和返回按钮右侧对齐)。 post
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:padding="16dp"
android:background="@color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_avatar"
android:layout_marginRight="15dp"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="fitXY"
android:src="@drawable/img_avatar" />
<TextView
android:id="@+id/tv_name"
android:layout_toRightOf="@+id/iv_avatar"
android:layout_toLeftOf="@+id/tv_price"
android:layout_marginRight="15dp"
android:singleLine="true"
android:maxEms="8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="朱霸天"
android:textColor="#FF222222"
android:textSize="15sp"
/>
<TextView
android:id="@+id/tv_price"
android:gravity="center_vertical"
android:layout_alignParentRight="true"
android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="+50"
android:textColor="@color/red"
android:textSize="15sp"
/>
<TextView
android:layout_alignBottom="@+id/iv_avatar"
android:layout_toRightOf="@+id/iv_avatar"
android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF9C9FA2"
android:textSize="13sp"
tools:text="2018-08-28"
/>
</RelativeLayout>
复制代码
上面的代码,在320dp到411dp的手机上都是适配了的;代码也很简单。这里个人确是作了适配的,还原了控件之间的关系(价格在屏幕右侧,标题和时间在头像右侧);设计图上名字“朱霸天”是三个字,实践上TextView都要考虑内容过多换行或挤到右侧的问题(名字挤到右侧红色价格,会重叠),因此须要限制为SingleLine和maxEms,名字控件必须是在价格控件左侧(android:layout_toLeftOf="@+id/tv_price")。字体
上面说到手机屏幕适配就是适配320到411dp的设备,这个数据可能不许确。手机屏幕众多,这只是我看到的大部分的屏幕。但这个并不影响适配的效果,就算是扩展到480dp甚至平板的600dp,按dp适配也是能兼容的。固然有些效果可能不是太好,这更应该是设计问题,而不是适配问题,毕竟有些UI的确不适合在大屏上显示,内容会显得太空。虽然不是很准确,但知道这个很重要。好比你不能看到一行能够显示50个文字写死了一个TextView的宽度,毕竟你看的效果多是360dp的,在320dp上一行50个字会放不下。
DP适配后彻底不用管屏幕像素也不太准确,毕竟该作的仍是要作的。该作的也说过了,给不一样xxxxdpi的文件夹放对应的资源文件。
在高度的处理上,前面也说到如今UI通常认为高度是无限延长的。若是是一个特别长的列表,适配时会使用ListView或ScrollView实现无限延长效果。要当心的是一个看起来是一个屏幕大小的布局,不能假设页面必定有640dp高或者其余。原本Android屏幕的高度就不同;有些设备的系统按键多是虚拟的,会占据一部分屏幕;Android 添加了分屏效果(multi-window)后,屏幕高度可能不是App窗口的高度了。
DP适配固然不是什么问题都没有。好比实现一个启动页面上放一张满屏的图片,要兼容像素和宽高比例的屏幕的话很难保证不拉伸。这些问题又变成了一个设计问题,我近来看到像网易云音乐或QQ音乐启动广告页底部都留了一截显示App名称,让图片自带宽高比例,这种设计就很好。对于Android而已,整个系统是基于DP机制的,甚至还有sp机制,整个适配也并不复杂。ui
没有了;看状况待续。spa