Android UI适配那点事

       近日在QQ群里,好几个小伙伴都问起android UI适配的事。有的是不知道怎么去做适配,有的是对计量单位的概念不清晰。在此拟文,希望能起到抛砖引玉之功效。

问题1:为什么要做适配?

       感觉问题1,有点二吧。为什么要做适配?因为别人的程序都能在各种机型完美运行。我的也要…哭。谷歌的android 是开放的,各个生产厂商可以定制自己的硬件参数,和软件系统,就导致碎片化超级严重。特别是国产的机型,各种尺寸,各种分辨率,有木有?如果谷歌规定了,只能用一种规格。如 手机 是4 英寸 720x1280 。那么我们只针能一这个规格写程序,就不存在适配的问题了。扯远了……

       做UI适配置的首要的原则是:“视觉效应” ,其次是操作的简便性,合理性。何为视觉效应,也就是说,你的产品做出来之后,整体的感觉,是可以让你的大部分用户感觉到舒适,美观。视觉效应是一个很主观的东西。比如一个UI的字体是 15 ,年轻人可能感觉正合适,而上了年纪人,可能感觉有点偏小。如是一个外星人(这个外星人的视力比人类强10倍),那么刚才那个字体,外星人可能会感到太大了。

     好吧,回到技术层面。Android 在长度单位,可以是以下几种:px、dp(dip)、sp。其中 px 即像素,dp和sp 是android特有的单位。是谷歌创造的。在底层的绘制时,最终还是要转成px。那么为什么要用dp、sp作为长度单位呢?原因就是:各个设备硬件的参数的差异化。如果你直接用px作为单位,你会发现,在一些机型上效果很好,另一些机型上,就不是那么美观了。下面来个图,说明这种情况:

有小米2 ( 4.7' 720*1280),山寨小米2(4.7' 480*800) ,有一个应用,就是相片查看,现在要做一个缩略图的查看,点击后,可以看大图。

ImageView 是200px * 400px ,在上述的设备,效果图如下。


现在把ImageView 的px换成dp.效果图如下:



看到差别了。其实,用dp,sp的主要目的,是为了让UI里的元素,在不同设备的视觉效应,看上去都差不多


这里只有两个设备,UI元素也很简单,不就一个ImageView吗,这有什么好扯的。是的,没有技术含量。

--------------------------------------------------------------

相关概念

分辨率:整个屏幕的像素数目,为了表示方便一般用屏幕的像素宽度(水平像素数目)乘以像素高度表示,形如1280x720,反之分辨率为1280x720的屏幕,像素宽度不一定为1280

屏幕密度:表示单位面积内的像素个数,通常用dpi为单位,即每英寸多少个像素点.

 注意:这里的dpi是指物理的密度(即每英寸像素数,如120dpi,160dpi等,假设QVGA(320*240)分辨率的屏幕物理尺寸是(2英寸*1.5英寸),dpi=160)一般来说,同样的尺寸的屏,密度越高,分辨率也越高,精细度(视觉效应)也越高。

注意:不要把dpi和dip搞混了,一个是物理的,一个是逻辑的。dip=dp 在本文中,这不用dip作为示例,统一用dp


px:长度单位,以具体像素为单位

dp:长度单位,与具体屏幕密度无关,显示的时候根据具体平台屏幕密度的不同最终转换为相应的像素长度,具体转换规则是: 1dp = (目标屏幕密度/标准密度)*px 。

标准密度为160dpi,例如,1dp长度在密度为160dpi的平台表示一个像素的长度,而在240dpi的平台则表示1.5个像素的长度

px和dp的换算公式:px= dp*density   即:px=dp * (设备DPI / (float) 160)


屏幕尺寸:屏幕的大小,通常用屏幕对角线的长度表示(英寸)

密度比例: density=DENSITY_DEVICE / (float) DENSITY_DEFAULT  即 :设备密度 / 默认密度(160)


Android界面适配机制

UI界面在不同平台的适配受屏幕尺寸屏幕密度影响,Android适配机制就是在资源后面添加对这两种因素的限定,通过不同的限定区分不同的平台资源,Android在使用资源的时候会优先选择满足本平台限定的资源,再找最接近条件的,再找默认(即不加限定),通过选择适合当前平台的资源来完成不同平台的适配。


谷歌把屏幕尺寸分为:small,normal,large,xlarge分别表示小,中,大,超大屏

屏幕密度分为:ldpi,mdpi,hdpi,xhdpi,它们的标准值分别是:120dpi,160dpi,240dpi,320dpi (现在的设备已有xxhpdi,还有其他一些 如tvdpi)

以上划分均表示的是一个范围:


PS:谷歌为什么要这么分呢?说白了,还是为了UI适配。刚才那两个机型,分别对应 normal大小的屏(4.7),米2 的dpi 是312 (xhdpi),山寨的 dpi是 198(tvdpi) (这些值有误差..)

看到这里,请不要感觉到奇怪,为什么手机也会有198这样的dpi (不要纠结实现生活有没有这个东西),这说明了,一样大小的屏,由于生产手机的厂商,做出来的屏,密度一不样,最终设备的像素也会有所差别。谷歌想到这点,就整出上面那个 屏幕尺寸的划分,和一个称为“dp”的怪东西……

有了这个划分,我们就能以最小的工作量,完成大部分设备的适配。

粗适配

在资源目录后面加上上面的限定就能为资源指定特定的适用平台,如下所示


在实际开发过程中屏幕尺寸不够直观,android将其转换为分辨率(dp)表示,根据屏幕具体分辨率(dp)可选择相应的限定符

注意:这里用了dp ,并非是px ,就是说你的设备,如果达到了426dp x 230 dp 就是小屏 。


上面这是针对布局的。如果你偷懒只做一个布局文件资源,并且布局不是那么复杂,那么,你可以在values文件夹里,建立多个限定资源的dimens文件(dimesn 定义的是如字体的大小,控件layout的高、宽、padding 等等的长度的值 ,这些值,建议用dp、sp,原因如上述示例。)如下所示:

针对不同资源的dimens,调整其中定义的值,也可以达定适配置的效果,当然,多个 限定资源的layout和 多个限定资源的dimens 也可以一块使用。


小结:通过加上上述限定可以实现一个apk适配几种主流的屏幕尺寸和屏幕密度,这种限定方式比较适用于对外发布应用,不知道终端具体参数的情况,但是不能做到精确适配,对于屏幕尺寸和密度相差不大的两种平台不能很好的区分。


精确适配

为了解决上述问题,自Android3.2开始,引入了精确适配,理论上可以适配任意像素宽度,高度,屏幕密度的平台,需用以下方式添加限定符。


其中w1280dp表示屏幕宽度为1280dp,h752dp表示屏幕高度为752dp,160dpi表示屏幕密度,其中屏幕宽,高必须以dp为单位,在知道屏幕像素宽高度的情况下可以通过公式:1dp = (目标屏幕密度/标准密度)*px 转换成dp单位。

例如:某平台屏幕宽,高分别为1920px,720px,屏幕密度为240dpi

或者是:

根据公式1dp=(240/160)px=1.5px,宽度,高度转为dp单位分别是1280dp和480dp.

注意:这里高的值480dp ,指的不是设备的高度?而是你的UI需要的最小的高度?(这里有点迷糊)。高度不是必须的,一般的程序无需指定这个高度的值,因为,竖向一般是可以滚动的,更需要关注的是横向的值。(指定了,你会发现,如果计算错误,将是十分的蛋疼。

注意:网上的资料说:当rom不把虚拟键计算到屏幕尺寸时,你自己计算的高度,和资源的限定符对不上时,可能适配置到下一个资源。


限定符还可以是 sw<N>dp和w<N>dp。其中sw<N>dp  是指屏幕的基本尺寸,具体来说,是指屏幕最短的尺寸,不论是宽,还是高。当然,你也可以认为它是指定的是宽度。如果,你一个程序,需要600dp 那么你可以这样写:layout-sw600,不论高还是宽。这个值,不会因为屏幕的旋转而变化。(The smallestWidth is a fixed screen size characteristic of the device; the device's smallestWidth does not change when the screen's orientation changes.)虽说,不管限管的是宽,还是高

,但是你还是要更多关注的是宽度,因为UI通常会垂直滚动(别问为什么)

w<N>dp 是指定所需要的最小的宽度,当屏幕方向发生改变时,会以反应当前实际可用的宽度供你的UI使用(这可能会导致切换到别的限定符的资源)。h<N>dp和 w<N>dp类似,不过,我们一般不需要关注它。


PS:这里是要带单位(dp)的。以下是错误的定义:layout-1280x480、layout-w1280-h480、layout-w1280px-h480px、layout-w1280px-h480px-hdpi


------------------------------------------------------------------------

关于 drewable-ldpi 、 drewable-mpi、 drewable-hdpi、 drewable-xhdpi、 drewable-xxhdpi
这几个目录,是用于存放不同密度的图片。系统会根据机器的分辨率来分别到这几个文件夹里面去找对应的图片

drawable-mdpi里面存放中等分辨率的图片,如HVGA (320x480)

这里所指的图片,主要是针无法用.9处理的图片,如一些程序启动时引导的图,你再怎么用.9处理,如果你没有几套对应的图,在不同的设备上,都会产生放大或缩小的情况。

如果是一些控件的Selector、背景之类的资源图片,用.9格式的图,只有一套资源,也是没有问题的。


关于横屏切换

有两种情况:1、不切换。2,切换

不切换的,就是一个方向,要么横屏,要么屏;切换的,要考虑不同方向的布局视觉效果。如是以横屏为原形设计的,切换成屏时,可能会有一些控件出现过小、或是不够位置放,这时,可以利用HorizontalScrollView 之类作为解决手段,切不可把控件删除,否则切换时,找不到控件,会报错。


关于手机和平板的适置

手机一般来说,都是4~5英寸,当然也有个别的,达到7英寸。平板一般是6-12英寸。如果想要在一个apk中,完成手机和平板的适配。按

这种,基本可以解决,但是由于平板一般是横屏的,手机一般是坚屏的。这两者的界面,可能不太一样,如手机,可能没有侧面的菜单,而平板上是有的。

这种,可以用Fragment来解决。(ApiDemo FragmentList好象实现了这个效果)

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


验证计算公式,可以跳过………………大笑

在上面设备的换算分别是(大概):1dp=1.95px ,1dp=1.2px (有误差...)

我们在程序输出一下,验证一下。点击ImageView 输出一下相关的信息。

代码如下:

	OnClickListener ivPhotoClick = new OnClickListener() {

		@Override
		public void onClick(View v) {

			DisplayMetrics dm = getResources().getDisplayMetrics();
			float density = dm.density;
			int densityDpi = dm.densityDpi;
			int heightPixels = dm.heightPixels;
			int widthPixels = dm.widthPixels;
			float scaledDensity = dm.scaledDensity;
			float xdpi = dm.xdpi;
			float ydpi = dm.ydpi;
			
			int imageHeight=ivPhoto.getHeight();
			int imageWitth=ivPhoto.getWidth();
			
			
			System.out.println("密度比例:density-->"+density);
			System.out.println("密度:densityDpi-->"+densityDpi);
			System.out.println("设备高(像素):heightPixels-->"+heightPixels);
			System.out.println("设备宽(像素):widthPixels-->"+widthPixels);
			System.out.println("字体的密度:scaledDensity-->"+scaledDensity);
			System.out.println("设备x方向dpi:xdpi-->"+xdpi);
			System.out.println("设备y方向dpi-->"+ydpi);
			
			System.out.println("控件高:imageHeight-->"+imageHeight);
			System.out.println("控制宽:imageWitth-->"+imageWitth);
			

		}
	};

米2 输出 如下:


山寨输出如下:

值有些误差,不要在意这些细节。

---------------------------------------------------


限定符使用示例(个人感觉精确适配这个不好控制,意义不大,不如 layout-larger-mdpi 这种类型来得实际)

如上图所示:分别是 sw360dp-h200dp-xhdpi 对应米2. layout-w360dp-tvdpi

在之前的ImageView下方,加入一个TextView控件,值分别为:默认,米2、山寨。如下图


坚屏


横屏:山寨(M2-Copy)由于没用sw 所以,当屏幕方向变化时,宽的值发生变化,导致了布局的切换(变成默认)。但是TextView的文字位置都错误了。


把限定符再次更改:都成了sw360的资源






总结:

1.清楚需适配的设备都有哪些规格,列表分类,划分出类似出来谷歌那个表格。

2.控件的单位用dp ,文字用sp,并且在dimens中定义。你可以有多个dimens资源。可以用wrap_content, fill_parent,这类值让系统自己计算。

3.合理设计UI界面,再牛B的适配方案,也敌不过你在w600dp的设备上,放在一堆,并且都是固定值的控件(60dp 20个,横向排,不滚动的情况下,UI会烂到爆)

4.UI调节时,拉上美工,TA能给出更有建设性的想法。

5.合理运行资源限定符,不一定非要精确适配。UI就是一个视觉效应。有时,粗适配,效果也很好。

6.如果以上都不能解决问题,请尝试精确适配。奋斗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值