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适配方案
由于今日头条方案直接修改DisplayMetrics#density、DisplayMetrics#densityDpi、DisplayMetrics#scaledDensity会导致系统的View尺寸和第三方尺寸的大小与原来不一样,所以选择pt适配则不会使原项目的ui发生改变。
优点:
- 无侵入性
对以前使用的dp、sp无任何影响
- 灵活性高
使用pt则适配,使用dp、sp则与以前一样
- 不影响系统View和第三方View
没有修改DisplayMetrics#density、DisplayMetrics#densityDpi、DisplayMetrics#scaledDensity
- 不会失效
某些系统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