Android里神奇的dp

看了这篇文章,我的心得有几个:

1、之所以使用dp,是为了保证控件的长度一致

2、像素一样,dpi不一样,那么长度不同

3、dp一样,dpi不一样,长度相同


所以相同的dp在不同的手机上看起来长度是一样的,而我在设置间隔的时候如果设置为具体数值的dp,那么其间隔长度也是一样的,这样比较小尺寸的手机就会出问题了


屏幕分辨率
首先要了解Android屏幕分辨率,从density来看,常见的分辨率对应关系有:
hdpi = 480x800 (如Nexus One/Nexus S)
mdpi = 320x480 (如HTC Wildfire S G13)
ldpi = 240x320 (如HTC G8)
除此之外,还有xhdpi = 960x640 (如魅族M9)
但以上只是常见的而已,实际分辨率像素数量与density无关,例如SonyEricsson X10分辨率是480x854也是hdpi,Kindlefire 的分辨率是 1024x600但实际是mdpi(large-mdpi)。另外还有各种山寨诡异的屏幕分辨率,暂且不说。

更多的参数关系可以参考:
http://developer.android.com/guide/practices/screens_support.html

什么是DP
有了以上知识之后,再看看dp。dp的意义是按照mdpi下px:dp = 1:1换算,其他分辨率按照density比例算出来(xhdpi=320,hdpi=240,mdpi=160,ldpi=120),如果不想记density值,则按照标注屏幕宽度比例换算也可以(对large-*dpi无效),也就是说:
mdpi下屏幕宽度为320px,所以就是320dp,半个屏幕自然就是160dp
hdpi下dp:px = 1:1.5 (按照屏幕宽480/320=1.5算出来),所以160dp也等于半个屏幕宽
ldpi下dp:px = 3:4 = 1:0.75 (按照240/320=0.75算出来),所以160dp也等于半个屏幕
……
其他算法也一致,所以对于常见的标准分辨率来说半个屏幕宽度就是160dp。

其他可能的问题
但仍需要考虑很多特殊情况,例如:对于像Kindlefire这种large-mdpi来说,160dp=160px,而人家实际宽度为600,就肯定是不对的。如果横屏,横屏对于480x800的hdpi的一半是400px/1.5 = 267dp,而对于480x854的hdpi的一般则是427px/1.5 = 285dp,所以会出现很多问题。

动态代码处理
所以,最为保险的方式,是在代码中,调用:


   
   
  1. DisplayMetrics metrics = new DisplayMetrics();
  2. getWindowManager().getDefaultDisplay().getMetrics(metrics);
  3. int screenHalfWidth = metrics.widthPixels / 2;

然后修改view的大小:


   
   
  1. view.setLayoutParams(new LayoutParams(screenHalfWidth, LayoutParams.WRAP_CONTENT));

关于DisplayMetrics的使用,可以参考http://developer.android.com/reference/android/util/DisplayMetrics.html 它m里面可以获取很多与屏幕相关的内容,包括density比例,当前density等




——————————————————————————————————————————————————————————————————————

今天偶然间问了同事一个关于dp单位的问题,然后由这个问题引发的一连串的问题彻底颠覆了我关于dp的理论体系。


我那个问题是这样的:既然dp的本质是物理尺寸,为什么不用cm或者mm等传统长度单位替代?


然后他回答我dp是和像素密度无关的。。。我对这个回答不屑一顾,不过他接下来的一句话把我彻底震惊了,那句话是这样的:在你的手机上320dp就刚好满屏了,310dp就差一点点满屏。


我的手机是HTC Desire,这个理论我闻所未闻,然后马上做了个小实验,事实确实是这样,把一个TextView背景设成红色,宽度设成320dp,能看到满屏,310dp就差那么一点点。


看到这个测试结果的时候,我再一次崩溃了,我希望同事第二句话是一个美丽的错误,我无法接受这么久以来我理解的东西是错误的,可是事实是残酷的。


Android Developers关于dp的文档我看过N次,那个px和dp的转换公式我记得很清楚: px = dp * (dpi / 160),可是今天翻了源码了才发现,原来这里的dpi是归一化后的dpi,可能值只有120(low)、160(medium)、240(high)、320(xhigh)四种,而我之前理解的竟然是实际设备真实的dpi!


G7的真实dpi是252,根据我以前的理解,310dp换算成px应该是:310 * (252 / 160) = 488像素,而G7水平方向是480px,310dp在这上面绝对满屏都不止了,事实上Android系统并没有拿252作为dpi来计算,而是将G7视作hdpi设备,然后使用240dpi来计算最终像素,所以在G7上320dp刚好是:320 * (240 / 160) = 480像素,刚好满屏了,310dp确实要差一点点。


搞清楚这个问题后,我心里稍微好受点了,可是另一个问题接踵而来:
dp的本质还是物理尺寸,难道不是吗?尽管Android系统对待dp这事上,将所有设备视为四种:120(low)、160(medium)、240(high)、320(xhigh),在160dpi上,160dp就是1英寸,在240dpi上,160dp还是1英寸,120dpi和320dpi也还是1英寸,虽然他们占用的像素数不一样,但是最终显示出来的效果都是占用了屏幕上1英寸的范围。这套体系其实是非常合理的,一个宽为160dp的按钮,它在所有设备上占用的物理尺寸应该是一样大才合理,这样他们对人眼所形成的张角才一样大,观看或者阅读的感觉才一致(姑且不考虑按钮的背景是否一样细致)。这应该是Android系统引入dp概念的原因,因为手机屏幕的像素密度相差实在太大了,web那套东西已经完全不适用,你想电脑屏幕的像素密度能相差多大?


终极问题来了,现实生活中真的只有以上四种不同像素密度的设备吗?不可能。虽然所有这些设备都可以粗略地划分为low、medium、high、xhigh四种密度,可是对于划在同一范围内但具有不同像素密度的两个设备来说,同样的dp最终所占用的物理尺寸是不一样的。举个例子,G7(HTC Desire)的屏幕尺寸是3.7英寸,分辨率是480*800,像素密度是252dpi,G10(HTC Desire HD)的屏幕尺寸是4.3英寸,分辨率同为480*800,像素密度是217dpi。假设现在有一个按钮,它的宽度设为100dp,由于G7和G10同被划分为hdpi,那么在这两个设备上,这个按钮的实际宽度是:100 * (240 / 160) = 150像素,可由于这两个设备的实际像素密度是不一样的,所以真实的显示效果是:这个按钮在两个设备上的实际物理尺寸是不一样大的。而这,和Android进入dp的概念是相悖的。


你可以说对于同属于hdpi的设备而言,这个差别很小,但是在ldpi和hdpi之间这个差别就很明显了。我非常认同,可是如果在将dp转化为px的时候,不是使用归一化dpi(也就是120(low)、160(medium)、240(high)、320(xhigh)这四种),而是使用设备真实的像素密度,那么得出的像素数目虽然各不一样,但是最终显示出来的物理尺寸确实一样大的,而这种计算方法,我认为是忠于像素密度无关的理论的。


最后我还是想说,如果Android希望一个宽度为160dp的按钮在任何设备上都是1英寸大,那为什么不直接使用英寸作为度量单位呢?


如果你有好的想法,欢迎留言。



UPDATE:

今天下午在回答factar网友的问题的时候,我上面那个“终极问题”终于找到了一个合理的答案。在factar贴的网址里,我发现一句重要的话:

However, bitmap scaling can result in blurry or pixelated bitmaps, which you might notice in the above screenshots.

这句话的意思是说,图片资源在缩放的时候会造成图像模糊。按照我以上的分析,如果为了保证相同的图片资源在不同像素密度的设备上保持完全一样的尺寸大小(这完全可以做到,在dp转化成px的时候使用设备的物理像素密度参数),那图片在不同设备上的缩放因子必然不一样,而这会导致图像模糊!所以我猜想Google为了保证了图像不会模糊退了一步,让相同dp在不同设备上“差不多一样大”。


还有,这个答案也纠正了我的一个误区,现在有很多应用程序开发商为了降低安装包的大小,只使用一套hdpi资源或者一套xhdpi资源,而不提供mdpi资源或ldpi资源,希望在mdpi和ldpi设备上有系统完成缩放适应,虽然可行,但是我们不应忽视因为缩放带来的图像模糊、显示效果不佳的现象。


### 回答1: Android A2DP是指在Android操作系统上支持的无线音频分发协议(A2DP)。A2DP是一种蓝牙规范,旨在通过蓝牙无线技术从音频源设备(如手机、平板电脑)将音频流传输到音频接收设备(如耳机、扬声器)。 Android A2DP允许用户通过蓝牙连接将音频从手机或其他设备传输到外部音频设备,实现无线音频播放。通过与A2DP兼容的耳机或扬声器,用户可以在不使用有线连接的情况下享受高质量的音频体验。 使用Android A2DP时,用户可以通过蓝牙将音频源设备与音频接收设备配对并连接。一旦连接成功,音频源设备会将音频流传输到音频接收设备,用户可以通过耳机或扬声器听到音频。 Android A2DP既支持单声道音频流,也支持立体声音频流。这意味着用户可以在支持的设备上享受高保真音频体验,无论是听音乐、看电影还是进行语音通话。 总之,Android A2DPAndroid操作系统上支持的一种蓝牙音频传输协议,用户可以通过它将音频从手机传输到蓝牙耳机或扬声器,实现无线音频播放。这一功能为用户提供了更方便、更灵活的音频体验。 ### 回答2: A2DPAndroid操作系统的一种蓝牙音频传输协议,全称是Advanced Audio Distribution Profile。它允许用户通过蓝牙无线技术将音乐和其他音频内容从移动设备(例如手机、平板电脑)传输到蓝牙音频输出设备(例如耳机、音响等),从而实现无线音频播放。 使用A2DP功能,用户只需将蓝牙音频输出设备与移动设备进行配对连接,并选择将音频源从移动设备传输到蓝牙设备,即可实现无线传输音乐和其他音频内容的功能。A2DP不仅可以传输音乐,还可传输来自各种应用程序(如音乐播放器、视频播放器)的声音。这意味着用户可以享受到更自由的音频体验,无需担心被有线耳机限制。 使用A2DP还能给用户带来更多便利。例如,当用户听音乐时,他们可以将手机或平板电脑放在一个安全的位置(如背包或桌面),而无需将有线耳机连接到设备上。这样可以减少因长时间使用有线耳机而导致的疲劳感。此外,用户还能通过A2DP功能将手机与汽车音响系统连接,从而实现在驾车时无线播放来自手机的音乐、导航指示等功能。 总之,Android A2DP是一种非常实用的功能,它为用户提供了无线音频传输的便利,让用户在各种场景下都能享受到高质量的音乐和其他音频内容。 ### 回答3: Android的A2DP是指Advanced Audio Distribution Profile,即高级音频分发协议。它是一种蓝牙音频传输协议,用于无线传输高质量的音频数据。 在Android设备,A2DP协议允许用户通过蓝牙连接将音频从手机、平板电脑或其他设备传输到支持A2DP的设备,如蓝牙耳机、蓝牙扬声器等。这使得用户可以无线地享受高质量的音乐、电影、游戏等。 使用A2DP协议的好处之一是方便性。用户只需将手机或其他设备与支持A2DP的设备进行配对,就可以在不使用有线连接的情况下传输音频。这不仅提供了更大的灵活性,而且避免了繁琐的有线连接。 另一个优点是音质。A2DP协议支持高质量的音频传输,允许用户以CD音质或更高的音质享受音乐。这使得用户可以获得更好的听觉体验,而无需携带额外的有线设备。 但是,A2DP也有一些潜在的问题。首先,由于蓝牙技术的限制,传输的音频可能会有一定的延迟。这意味着在观看视频或玩游戏时,音频可能与视频或游戏有轻微的不同步。其次,传输的音频质量可能受到蓝牙信号强度和干扰的影响。在信号弱或有干扰的环境,音质可能会受到影响。 总的来说,Android的A2DP协议为用户提供了便利的无线音频传输解决方案。虽然可能存在一些潜在的问题,但对于大多数用户来说,A2DP仍然是一种非常方便和实用的技术。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值