Android屏幕适配不仅仅是dp那么简单

2018-11-10  本文已影响15人  奋飞的蜗牛ing

今日头条屏幕适配方案终极版正式发布!
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;
    }

原因:从而某些用户在系统中修改了字体大小失效了,但是我们还不能直接用原始的scaledDensity,直接用的话可能导致某些文字超过显示区域,因此我们可以通过计算之前scaledDensity和density的比获得现在的scaledDensity,方式如下:

final float targetScaledDensity = targetDensity * (appDisplayMetrics.scaledDensity / appDisplayMetrics.density);

解决:监听下字体切换,调用 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;
    }
上一篇 下一篇

猜你喜欢

热点阅读