前言:前段时间在一个技术网站中看到一篇文章:一种极低成本的Android屏幕适配方式,原文地址:https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA,拜读后感觉方法很独特,直接从源码着手解决问题,动手实践了一下,出现了一些问题,解决后感觉适配效果很好,于是推荐给小伙伴们使用,但是有人说看不懂,不知道怎么使用,所以决定写一点使用心得分享给大家,注:本文只教大家如何使用,如想了解实现原理请移步原文地址
首先来看原文最终方案代码:
你没有看错,就是这么简单,就这些代码就能实现适配???,是不是感觉贼不靠谱的样子,好吧,其实我也不信,于是照着这张图片手打下来(ps:真是纯手打的,因为他的原文终极解决方案代码就是一张图片...放心我不会也这么给你们一张图片的,待会儿放源码),写在BaseActivity中,
在这里要注意了:appDisplayMetrice.widthPixels/360,这个360是你设计图的宽度的dp大小,一定要根据你设计图的标准来写!!!
那这个设计图的宽度dp怎么根据自己的设计图上的标准来得到呢,很简单,只要得出设计图的比例density就O98K了
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
6.2018.11.12日补充,已有大佬封装好了该功能,使用后个人感觉他封装的很好,大家可以移步https://github.com/JessYanCoding/AndroidAutoSize ,各位慢走不送~~~