前言:前段时间在一个技术网站中看到一篇文章:一种极低成本的Android屏幕适配方式,原文地址:http://www.apkbus.com/blog-822415-77856.html,拜读后感觉方法很独特,直接从源码着手解决问题,动手实践了一下,出现了一些问题,解决后感觉适配效果很好,于是推荐给小伙伴们使用,但是很多都说看不懂,不知道怎么使用,所以决定写一点使用心得分享给大家,注:本文只教大家如何使用,如想了解实现原理请移步原文地址

首先来看原文最终方案代码:

 一种极低成本的Android屏幕适配方式

你没有看错,就是这么简单,就这些代码就能实现适配???,是不是感觉贼不靠谱的样子,好吧,其实我也不信,于是照着这张图片手打下来(ps:真是纯手打的,因为他的原文终极解决方案代码就是一张图片...放心我不会也这么给你们一张图片的,待会儿放源码),放在基类(BaseActivity)中,

在这里要注意了:appDisplayMetrice.widthPixels/360,这个360是你设计图的宽度的dp大小,一定要根据你设计图的标准来写!!!

那这个设计图的宽度dp怎么根据自己的设计图上的标准来得到呢,很简单,只要得出设计图的比例densityO98K了 

density的计算公式为:density = dpi / 160想要得到density,那就必须要先得到dpi,而dpi计算公式为:

 

好了,公式咱都有了,那么就开始套公式吧,比如我现在的设计图的分辨率为750*1334,屏幕尺寸为4.7(不知道的可以去问UI)

根据公式得出dpi= 326 ,那么density =326 /160 ≈= 2 ,很好,density有了,那么上图中的设计图的宽度dp就为 750/2 = 375

附上代码:

private static float sNoncompatDesity;
private static float sNoncompatScaledDesity;
private static  void setCustomDesity(@NonNull Activity activity, @NonNull final Application application){
    final DisplayMetrics appdisplayMetrics = application.getResources().getDisplayMetrics();
    if(sNoncompatDesity==0){
        sNoncompatDesity = appdisplayMetrics.density;
        sNoncompatScaledDesity = appdisplayMetrics.scaledDensity;
        application.registerComponentCallbacks(new ComponentCallbacks() {
            @Override
            public void onConfigurationChanged(Configuration configuration) {
                if(configuration!=null&&configuration.fontScale>0){
                    sNoncompatScaledDesity = application.getResources().getDisplayMetrics().scaledDensity;
                }
            }

            @Override
            public void onLowMemory() {

            }
        });
    }

    final float targetDesity = appdisplayMetrics.widthPixels/375;//375 设计图的宽度dp
    final float targetScaleDesity = targetDesity*(sNoncompatScaledDesity/sNoncompatDesity);
    final int targetDesityDpi = (int)(160*targetDesity);

    appdisplayMetrics.density = targetDesity;
    appdisplayMetrics.scaledDensity = targetScaleDesity;
    appdisplayMetrics.densityDpi = targetDesityDpi;

    final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
    activityDisplayMetrics.density = targetDesity;
    activityDisplayMetrics.scaledDensity = targetScaleDesity;
    activityDisplayMetrics.densityDpi = targetDesityDpi;
}

 

然后在BaseActivity中的onCreate方法中调用该方法,项目跑起来运行在手机上,好像有点毛病,比平常的尺寸要小了点,于是多运行了几款测试机,大部分手机貌似都有这个问题,而且在其中一款手机上显示的特别小,适配的效果明显不对,怎么办,这种适配方式不行吗,于是私信文章发布者,可惜没回复我,于是自己慢慢排查问题,折腾许久,终于发现了一个不起眼的问题

final float targetDesity = appdisplayMetrics.widthPixels/375;

问题出现在这行代码中,这行代码一眼看上去好像没问题,2个数字相除得到一个float类型的结果,但是appdisplayMetrics.widthPixels这个值是int类型,375也是int类型,int整数除以int整数得到的一定是int整数原来如此,就比如说我明明预想中得到的结果应该为1.8 ,结果却给我变为了1,这怎么可能不出问题呢,所以这行代码正确写法应该是这样的:

final float targetDesity = (float)appdisplayMetrics.widthPixels/375;

再次编译运行,适配完美!

最终代码:

private static float sNoncompatDesity;
private static float sNoncompatScaledDesity;
private static  void setCustomDesity(@NonNull Activity activity, @NonNull final Application application){
    final DisplayMetrics appdisplayMetrics = application.getResources().getDisplayMetrics();
    if(sNoncompatDesity==0){
        sNoncompatDesity = appdisplayMetrics.density;
        sNoncompatScaledDesity = appdisplayMetrics.scaledDensity;
        application.registerComponentCallbacks(new ComponentCallbacks() {
            @Override
            public void onConfigurationChanged(Configuration configuration) {
                if(configuration!=null&&configuration.fontScale>0){
                    sNoncompatScaledDesity = application.getResources().getDisplayMetrics().scaledDensity;
                }
            }

            @Override
            public void onLowMemory() {

            }
        });
    }

    final float targetDesity = (float)appdisplayMetrics.widthPixels/375;//375 设计图的宽度dp
    final float targetScaleDesity = targetDesity*(sNoncompatScaledDesity/sNoncompatDesity);
    final int targetDesityDpi = (int)(160*targetDesity);

    appdisplayMetrics.density = targetDesity;
    appdisplayMetrics.scaledDensity = targetScaleDesity;
    appdisplayMetrics.densityDpi = targetDesityDpi;

    final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
    activityDisplayMetrics.density = targetDesity;
    activityDisplayMetrics.scaledDensity = targetScaleDesity;
    activityDisplayMetrics.densityDpi = targetDesityDpi;
}

 

到了这里,有人就要问了,如果我这些代码都写好了,那么我在xml上面如何写dp大小呢?很简单,上面我们不是根据设计图的尺寸得出了density 等于2吗,那么比如设计图上标注一个TextView字体大小为30px 。则实际填写的字体大小为: 30 /2=15dp,根据这个规则来换算出来就好了

 

总结

1. 把最终代码放在BaseActivity中

2. 根据设计图尺寸得出比例density(如果实在不想算,想直接知道已知分辨率和屏幕尺寸设计图的density,请查看:https://material.io/tools/devices/,如果进不去,请翻墙)

3. 用设计图屏幕宽的分辨率/density得出该设计图宽的dp并赋值到原来的值中

4. BaseActivity的onCreate方法中调用

5. xml中的dp-size根据设计图尺寸得出的比例density的值来赋值,px-size/density = dp-size

 

拓展

上面的最终方案是以屏幕宽度来适配的,但是如果我想以高度来适配呢,比如有一个页面,在不使用ScrollView的情况下,想让所有的手机一屏就能显示完全,且不能有显示不全的情况,如果以宽度来适配的话是肯定会有适配问题的,所以应该要支持某些页面可以适当地使用以高度来适配:

public static float sNoncompatDesity;
public static float sNoncompatScaledDesity;
public static  void setCustomDesity(@NonNull Activity activity, @NonNull final Application application,boolean isWidth){
    final DisplayMetrics appdisplayMetrics = application.getResources().getDisplayMetrics();
    if(sNoncompatDesity==0){
        sNoncompatDesity = appdisplayMetrics.density;
        sNoncompatScaledDesity = appdisplayMetrics.scaledDensity;
        application.registerComponentCallbacks(new ComponentCallbacks() {
            @Override
            public void onConfigurationChanged(Configuration configuration) {
                if(configuration!=null&&configuration.fontScale>0){
                    sNoncompatScaledDesity = application.getResources().getDisplayMetrics().scaledDensity;
                }
            }

            @Override
            public void onLowMemory() {

            }
        });
    }
    final float targetDesity;
    if(isWidth){
        targetDesity = (float) appdisplayMetrics.widthPixels/375;//375 设计图的宽度dp 根据宽度适配
    }else{
        targetDesity = (float) appdisplayMetrics.heightPixels/667;//667 设计图的高度dp 根据高度适配
    }
    final float targetScaleDesity = targetDesity*(sNoncompatScaledDesity/sNoncompatDesity);
    final int targetDesityDpi = (int)(160*targetDesity);

    appdisplayMetrics.density = targetDesity;
    appdisplayMetrics.scaledDensity = targetScaleDesity;
    appdisplayMetrics.densityDpi = targetDesityDpi;

    final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
    activityDisplayMetrics.density = targetDesity;
    activityDisplayMetrics.scaledDensity = targetScaleDesity;
    activityDisplayMetrics.densityDpi = targetDesityDpi;
}

原理参考宽度dp,使用当前的设计图的高度px/density 就是当前的高度dp了,使用时,可以在BaseActivity中调用

setCustomDesity(this, App.getIntence(),true);

因为我们一般都是使用宽度来适配屏幕的,所以默认宽度适配,如果想在单独的Activity中使用高度适配时,可以在该Activity的onCreate方法中调用

setCustomDesity(this,App.getIntence(),false);

 

一种极低成本的Android屏幕适配方式

相关文章: