Android屏幕适配不仅仅是dp那么简单
今日头条屏幕适配方案终极版正式发布!
https://mp.weixin.qq.com/s/KJgEmrFRe2UJ0RszuO_9qg
首先看几个概念:
px = density * dp;
density = dpi / 160;
dpi=sqrt(H^2 + W^2)/屏幕尺寸【其中H、W为高宽,单位为px】
dpi是dot per inch,每英寸多少点,针对的是印刷页即打印到纸上或者应用到ui切的图片中;
ppi是 Pixel per inch,每英寸像素数,针对的是手机屏幕;
网上有dpi和ppi的关系,甚至还有给出两者之间的换算关系的。
产生dpi和ppi混乱的原因:ui切图的时候以dpi为准,而手机厂商给定的屏幕分辨率参数中给的是ppi。实际上ppi和dpi就概念上来说是没有一丁点关系的,更没有一个换算的关系。但是在这里有一个趋势上的相同点,就是dpi越大ppi越大,而且我们在换算px到dp或者图片放入哪个drawable文件夹中时候只能将ppi当成dpi(因为切图的大多以iphone为切图的基础屏幕,而iphone的官方参数也只能查到ppi,且即使我们用Android手机为切图基础屏幕,在java代码中获取dpi也是获取的一个DisplayMetrics类,中的一个固定的值,而且在换算的时候是1.5,2,3等倍数也是一个范围值)。
图片资源要放在哪个drawable文件夹中,从标注图中的px转化为dp都用的dpi而不是ppi。所以基本上开发的时候我们使用dpi就ok了。
原文:https://blog.csdn.net/gongxiaoou/article/details/78256344
所以:dpi = ppi吧??
DisplayMetrics#density 就是上述的density
DisplayMetrics#densityDpi 就是上述的dpi
DisplayMetrics#scaledDensity 字体的缩放因子,正常情况下和density相等,但是调节系统字体大小后会改变这个值
头条方案:
链接:https://juejin.im/post/5b1272b85188257d63225856【头条技术还真挺牛的哈】
假设设计图宽度是360dp,以宽维度来适配。那么适配后的 density = 设备真实宽(单位px) / 360
private static float sRoncompatDennsity;
private static float sRoncompatScaledDensity;
private void setCustomDensity(@NonNull Activity activity, final @NonNull Application application) {
//application
final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
if (sRoncompatDennsity == 0) {
sRoncompatDennsity = appDisplayMetrics.density;
sRoncompatScaledDensity = appDisplayMetrics.scaledDensity;
// 解决:系统设置中切换字体,再返回应用,字体并没有变化
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig != null && newConfig.fontScale > 0) {
sRoncompatScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
//计算宽为360dp 同理可以设置高为640dp的根据实际情况
final float targetDensity = appDisplayMetrics.widthPixels / 360;
// 解决:字体过小问题
final float targetScaledDensity = targetDensity * (sRoncompatScaledDensity / sRoncompatDennsity);
final int targetDensityDpi = (int) (targetDensity * 160);
appDisplayMetrics.density = targetDensity;
appDisplayMetrics.densityDpi = targetDensityDpi;
appDisplayMetrics.scaledDensity = targetScaledDensity;
//activity
final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
}
-
问题1:字体过小
原因:从而某些用户在系统中修改了字体大小失效了,但是我们还不能直接用原始的scaledDensity,直接用的话可能导致某些文字超过显示区域,因此我们可以通过计算之前scaledDensity和density的比获得现在的scaledDensity,方式如下:
final float targetScaledDensity = targetDensity * (appDisplayMetrics.scaledDensity / appDisplayMetrics.density);
-
问题2:系统设置中切换字体,再返回应用,字体并没有变化
解决:监听下字体切换,调用 Application#registerComponentCallbacks 注册下 onConfigurationChanged 监听即可。
在Activity中设置的方法,存在于此Activity下的fragment,dialog和PopupWindow都会受到此效果的影响,也就是说,在Activity中设置一次之后,Activity下的其他子View都无需再设置一次。
终极解决方案:添加上方向监听,使其能够自动变换适配宽或高
链接:https://juejin.im/entry/5b6d19b06fb9a04f87523527
// 注意适配高度时要减去barHeight
if (orientation.equals("height")) {
targetDensity = (appDisplayMetrics.heightPixels - barHeight) / 667f;
} else {
targetDensity = appDisplayMetrics.widthPixels / 360f;
}