android中的dp在渲染前会将dp转为px,计算公式:

  • px = density * dp;

  • density = dpi / 160;

  • px = dp * (dpi / 160)

而dpi是根据屏幕真实的分辨率和尺寸来计算的,每个设备都可能不一样的。

 

1.屏幕分辨率限定符适配

根据当前市面上手机的屏幕的分辨率创建不同的文件夹,系统运行的时候,会自动去选择读取对应的文件夹中的xml,即每种屏幕分辨率的设备需要定义一套 dimens.xml 文件

缺点是:假设我们UI设计图是按屏幕宽度为360dp来设计的,那么在上述设备上,屏幕宽度其实为1080/(440/160)=392.7dp,也就是屏幕是比设计图要宽的。这种情况下, 即使使用dp也是无法在不同设备上显示为同样效果的。 同时还存在部分设备屏幕宽度不足360dp,这时就会导致按360dp宽度来开发实际显示不全的情况
而且上述屏幕尺寸、分辨率和像素密度的关系,很多设备并没有按此规则来实现, 因此dpi的值非常乱,没有规律可循,从而导致使用dp适配效果差强人意。

2.今日头条适配方案(字节跳动)

在上面的两种适配方案中,都是根据不同的屏幕大小设置不同的控件的宽高值。在公式 px =dp * density 中,不同屏幕的dp,以及density都是不同,从而实现不同的手机渲染有着不同的px。在今日头条的适配方案的思路是假定每个手机的屏幕宽度是固定的。比如设计稿宽度是360dp, 想要保证在所有设备计算得出的px值都正好是屏幕宽度的话,只能修改 density 的值。

在xml中设置宽高后,都是通过以下代码进行转换:

http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/util/TypedValue.java#applyDimension

当在xml中设置的单位为px, 则直接用对应px的值;当单位为dp,则返回的是:value * metrics.density;当单位为sp,则返回的是value * metrics.scaledDensity。今日头条选用dp或者pt作为适配单位,给出的理由是项目中大部分都是使用dp做单位。

如果UI小姐姐给的设计图是 360dp(宽) x 640dp(高) 的设计图,如果要适配所有的屏幕,则 density = 设备屏幕的真实宽度(单位:px) / 360。这里为什么会是这样呢?上面公式中:px =dp * density,假定了所有的屏幕的宽度都是360dp,而px的值就是设备屏幕的真实宽度,所以就有了:density = 设备屏幕的真实宽度(单位:px) / 360。这样1dp在所有屏幕的宽中所占的比率都是一样的,都是1/360,在xml中就可以按照设计图来定义了。

而对于字体,Google推荐设置的单位为sp,在上面的源码中,字体的转换公式为:px= value * metrics.scaledDensity,这里的value 是在xml中定义的以sp为单位的值。一般情况下,scaledDensity 的值与 density 是相等的。但是如果在系统中设置了改变了字体的大小,scaledDensity 的值与 density 就不相等了。scaledDensity = 人为修改的density * (系统的ScaledDensity / 系统的Density)。如果不需要字体大小随系统设置而改变,就直接使用dp做单位好了.

    /**     * 使用 dp 作为适配单位(适合在新项目中使用,在老项目中使用会对原来既有的 dp 值产生影响)     * 
*
    * dp 与 px 之间的换算: *
  • px = density * dp
  • *
  • density = dpi / 160
  • *
  • px = dp * (dpi / 160)
  • *
* * @param context * @param designSize 设计图的宽/高(单位: dp) * @param base 适配基准 */ private static void matchByDP(@NonNull final Context context, final float designSize, int base) { final float targetDensity; if (base == MATCH_BASE_WIDTH) { targetDensity = sMatchInfo.getScreenWidth() * 1f / designSize; } else if (base == MATCH_BASE_HEIGHT) { targetDensity = sMatchInfo.getScreenHeight() * 1f / designSize; } else { targetDensity = sMatchInfo.getScreenWidth() * 1f / designSize; } final int targetDensityDpi = (int) (targetDensity * 160); final float targetScaledDensity = targetDensity * (sMatchInfo.getAppScaledDensity() / sMatchInfo.getAppDensity()); final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); displayMetrics.density = targetDensity; displayMetrics.densityDpi = targetDensityDpi; displayMetrics.scaledDensity = targetScaledDensity; }

    

 

从这两次的效果图,可以很明显的看出加了适配方案后,两个屏幕中控件的大小差不多一致了。此套方案对于原来的老项目不太友好,因为改了屏幕的density,所有布局的实际尺寸都会发生变化,整个布局中的所有尺寸都要进行修改一遍。

ps:以上被整理成了一个单独适配屏幕的类,下载地址是:https://download.csdn.net/download/and_caicai/12412196

参考:https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA

更多相关文章

  1. Android(安卓)- 支持不同的设备 - 支持不同的屏幕
  2. Android(安卓)Touch事件获取手指触摸位置
  3. android基础知识16:多分辨率屏显设计及其兼容性测试
  4. Android“UI适配技能”
  5. Android之机型适配
  6. 【Rayeager PX2分享】修改安卓开机后进入主屏幕流程分析
  7. Android碎片机制
  8. 你真的了解android:layout_weight吗?
  9. EditText输入框的长度调整

随机推荐

  1. Android学习-五布局之绝对布局(AbsoluteL
  2. Android帧布局实现霓虹灯的效果
  3. Android使用科大讯飞实现语音播报、语音
  4. 【Android】TextView倾斜文字
  5. android-logging-log4j - Logging with L
  6. android调用camera实现自定义照相
  7. android SD卡文件的读写(z)
  8. Android上通过gcc编译普通的C程序
  9. 分析android的usb-gadget
  10. Android ​自定义RadioButton或CheckBox