屏幕适配方案

1. 今日头条适配方案

直达:今日头条方案

分析过程:

dp和px的转换公式:px = dp * density
如果想要算出px正好是屏幕的宽度时,需要修改density,而density是DisplayMetrics的成员变量,通过 Resources#getDisplayMetrics 获得,Resource通过Activity或Application的Context获得。

先来熟悉下 DisplayMetrics 中和适配相关的几个变量:

  • DisplayMetrics#density 就是上述的density

  • DisplayMetrics#densityDpi 就是上述的dpi

  • DisplayMetrics#scaledDensity 字体的缩放因子,正常情况下和density相等,但是调节系统字体大小后会改变这个值

那么是不是所有的dp和px的转换都是通过 DisplayMetrics 中相关的值来计算的呢?

先看布局中的dp转换最终都调用了TypedValue#applyDimension(int unit, float value, DisplayMetrics metrics)进行转换:

public static float applyDimension(int unit, float value,
                                       DisplayMetrics metrics)
    {
        switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }

这里用到的DisplayMetrics正是从Resources中获得的。

再看看图片的decode,BitmapFactory#decodeResourceStream方法:

@Nullable
    public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
            @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
        ...
        
        if (opts.inTargetDensity == 0 && res != null) {
            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
        }
        
        return decodeStream(is, pad, opts);
    }

也是通过 DisplayMetrics 中的值来计算的。

最终方案:

假设设计图宽度是360dp,以宽维度来适配。
那么适配后的 density = 设备真实宽(单位px) / 360,接下来只需要把我们计算好的density 在系统中修改下即可。
详情见原文。

2. Blankj的pt适配方案

直达:Android 屏幕适配终结者

由于今日头条方案直接修改DisplayMetrics#density、DisplayMetrics#densityDpi、DisplayMetrics#scaledDensity会导致系统的View尺寸和第三方尺寸的大小与原来不一样,所以选择pt适配则不会使原项目的ui发生改变。

优点:

  1. 无侵入性

对以前使用的dp、sp无任何影响

  1. 灵活性高

使用pt则适配,使用dp、sp则与以前一样

  1. 不影响系统View和第三方View

没有修改DisplayMetrics#density、DisplayMetrics#densityDpi、DisplayMetrics#scaledDensity

  1. 不会失效

某些系统View会重置Resource或者创建新的Resource导致DM对象改变。最显著就是界面中存在WebView,由于其初始化的时候会还原DisplayMetrics#density 的值导致适配失效,虽然已有解决方案,但还是其他情况会还原这个值。这里解决了这个痛点。

使用:

依赖库:AndroidUtilCode
Blankj的android工具库,功能强大。

在gradle中添加依赖

implementation 'com.blankj:utilcode:1.30.6'
// if u use AndroidX, use the following
implementation 'com.blankj:utilcodex:1.30.6'

在Activity中重写getResource方法

override fun getResources(): Resources {
    return AdaptScreenUtils.adaptWidth(super.getResources(), 1080)
}

最后

推荐第二种方案,暂时没发现问题。

参考:
https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA
https://juejin.cn/post/6844903742135861261#heading-13
https://github.com/Blankj/AndroidUtilCode

你可能感兴趣的:(屏幕适配方案)