今日头条屏幕适配方案

一,如何使用

今日头条适配方案默认项目中只能以高或宽中的一个作为基准,进行适配。这里width可以是宽或高

1,全局使用,作用于整个项目,当然也可以只作用于局部,如:activity。其中width怎么得来的,以设计图1080*1920为例,按照XHDPI 2px=1dp,这样setDensity的值width就是540dp。凡是UI标注的px除以2就是布局中要写的dp值。

public class MyApplication extends Application {
    private float width;//设计图屏幕宽度 单位dp
    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("MyApplication", "onCreate: ");
        Density.setDensity(this, width);
    }
}

2,Density工具类代码

public class Density {
    private static float appDensity;
    private static float appScaledDensity;
    private static DisplayMetrics appDisplayMetrics;
    /**
     * 用来参照的的width
     */
    private static float WIDTH;

    public static void setDensity(@NonNull final Application application, float width) {
        appDisplayMetrics = application.getResources().getDisplayMetrics();
        WIDTH = width;
        registerActivityLifecycleCallbacks(application);

        if (appDensity == 0) {
            //初始化的时候赋值
            appDensity = appDisplayMetrics.density;
            appScaledDensity = appDisplayMetrics.scaledDensity;

            //添加字体变化的监听
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    //字体改变后,将appScaledDensity重新赋值
                    if (newConfig != null && newConfig.fontScale > 0) {
                        appScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {
                }
            });
        }
    }


    private static void setDefault(Activity activity) {
        setAppOrientation(activity);
    }

    private static void setAppOrientation(@Nullable Activity activity) {

        float targetDensity = 0;
        try {
            targetDensity = appDisplayMetrics.widthPixels / WIDTH;
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

        float targetScaledDensity = targetDensity * (appScaledDensity / appDensity);
        int targetDensityDpi = (int) (160 * targetDensity);

        /**
         *
         * 最后在这里将修改过后的值赋给系统参数
         *
         * 只修改Activity的density值
         */

        DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
        activityDisplayMetrics.density = targetDensity;
        activityDisplayMetrics.scaledDensity = targetScaledDensity;
        activityDisplayMetrics.densityDpi = targetDensityDpi;
    }


    private static void registerActivityLifecycleCallbacks(Application application) {
        application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                setDefault(activity);
            }
            @Override
            public void onActivityStarted(Activity activity) {
            }
            @Override
            public void onActivityResumed(Activity activity) {
            }
            @Override
            public void onActivityPaused(Activity activity) {
            }
            @Override
            public void onActivityStopped(Activity activity) {
            }
            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }
            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}

二,原理讲解

1,Android中px和dp的转换

我们知道在开发中,不管我们在布局中写多少dp,最后都会转化成px。

这里写图片描述

2,Android自身设定的屏幕密度

①,安卓尺寸众多,按每个屏幕去适配肯定不现实,所以为了解决这个问题,安卓手机屏幕有自己初始的固定密度,安卓会根据这些屏幕不同的密度自己进行适配

这里写图片描述
②,安卓分辨率五花八门,如何计算密度数。
这里写图片描述

3,传统的适配方式

经典的适配方式我们都是通过宽高限定符,在资源文件下生成不同分辨率的资源文件,然后在布局文件中引用对应的 dimens。但是这种方式未免太啰嗦,市面上有各种各样的手机,不能每种机型都去创建资源文件。

src/main
res
- - values
- - values-800x480
- - values-860x540
- - values-1024x600
- - values-1024x768
- - …
- - values-2560x1440

4,核心原理

①,density=当前设备屏幕总宽度(单位为px)/ 设计图总宽度(单位为 dp),为什么要算出density。在安卓源码TypedValue类中可以看到这段代码,我们通常用此方法进行dp和px的转化。density 的意思就是 1 dp 占当前设备多少像素,我们知道density 在每个设备上都是固定的。因此可以得出DPI / 160 = density,而屏幕的总 px 宽度 / density = 屏幕的总 dp 宽度,最终 dp=(px/dpi)*160

 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;
    }

②,由上面公式可以分别算出 屏幕宽度为1080和1440的dp,可以看出屏幕不同对应的总宽dp也不同。但是我们布局中的dp是相同的,这样就会导致控件在不同设备上所占比例不同出现变形,因此我们才需要适配。如何适配才能达到效果呢,如果保证让屏幕总dp宽度不变,就会达到适配的效果。

屏幕宽度1080,DPI 480 , 屏幕总 dp 宽度为 (1080/ 480 )* 160 = 360dp
屏幕宽度1440,DPI 560 , 屏幕总 dp 宽度为 (1440 / 560 )* 160 = 411dp

③,屏幕的总 px 宽度 / density = 屏幕的总 dp 宽度 , 可以看出px肯定是变化的,那么只能改变density 值才能保证dp不变。因此**density=当前设备屏幕总宽度(单位为px)/ 设计图总宽度(单位为 dp)**就派上了用场。

例1:设计图总宽度为375dp,屏幕宽度为1080px,可以得出density,1080/375=2.88。假如一个view为60dp60dp,那么算成px就是 60dp2.88=172.8px,所占屏幕宽度比为 172.8/1080=0.16

例2:设计图总宽度为375dp,屏幕宽度为1440px,可以得出density,1440/375=3.84。假如一个view为60dp60dp,那么算成px就是 60dp3.84=230.4px,所占屏幕宽度比为 172.8/1440=0.16

由示例1,2可以看出,虽然屏幕宽度不一样,但是都实现了屏幕等比例适配。由于计算没用到DPI,即使手机DPI不同也能完美实现适配

三,优缺点

1,优点

  • 使用起来简单,可以全局配置省去很多人工成本。
  • 使用系统API实现,减少性能损耗。

2,缺点

  • 如果是在老项目的基础上做会造成很大影响,不适合老项目。
  • 如果是第三方控件和自己的设计图不一致也会有影响。

总结:

  • 今日头条适配是以设计图的宽或高进行适配的,适配最终是改变系统density实现的。

  • 由于手机密度不同,通过修改系统密度,保证view所占比例不变,从而实现适配效果。

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值