Android 屏幕适配的几种方法

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 值产生影响)
     * <br>
     * <ul>
     * dp 与 px 之间的换算:
     * <li> px = density * dp </li>
     * <li> density = dpi / 160 </li>
     * <li> px = dp * (dpi / 160) </li>
     * </ul>
     *
     * @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
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值