这篇文章主要介绍了Android屏幕适配的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
测试与思考
不得不说今日头条的大神们的想法真的非常独到,成本极其低廉,还特别好用。他们给出的最终方案是这样的:
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; }
看到这篇文章之后我赶紧就写了一个demo测试了一下,发现了一点小问题。我们UI给出的设计图尺寸为1334*720,如果我按照宽度作为适配标准的话,按照设计图720px的宽度,屏幕的宽度应为360dp,也就是这样:
final float targetDensity = appDisplayMetrics.widthPixels / 360;
这样做的话宽度适配的比例是没有任何问的,但是我在想,如果需要以高度来做适配(也就是内容刚好纵向填充全屏)的话,是不是改成这样就可以了:
final float targetDensity = appDisplayMetrics.heightPixels / 667;
但是运行之后发现,高度上的差异很大,运行在不同分辨率和尺寸的手机上,页面中的每一部分内容在纵向上的比例不尽相同,没有达到很好的适配的效果。思考了许久过后我发现一个问题:我手边的测试机的宽度是两个720和两个1080,而高度有1280,1440,1780和一个全面屏的2160。Android的开原性导致了Android设备的尺寸的碎片化太严重,而通过查看手机的尺寸参数会发现,如果用这四个手机来测试的话,宽度可以直接整除,而高度不可以(并且我手边的测试机的宽度也可以整除,如果有宽度没法整除的手机呢?)。但是用今日头条给出的方法,做除法后结果会取整,那会不会是由于用纵向计算出来的density取整影响了精度,从而导致了效果不尽人意呢?
问题修复
发现上述问题之后我就着手去修改,将计算结果取余后在赋值给targetDensity,经过一下午的反复测试与实验,我重新修改了targetDensity的计算方法:
float targetDensity = 0; try { Double division = Operation.division(appDisplayMetrics.heightPixels, 667); //由于手机的长宽不尽相同,肯定会有除不尽的情况,有失精度,所以在这里把所得结果做了一个保留两位小数的操作 DecimalFormat df = new DecimalFormat("0.00"); String s = df.format(division); targetDensity = Float.parseFloat(s); } catch (NumberFormatException e) { e.printStackTrace(); }
经测试后发现,这样取两位小数计算过后,高度上的适配结果让人非常满意。可是还有一个问题,我们一般来说做适配都是以手机的宽度为基准,但是一个app里面避免不了偶尔一两个页面是按照高度为基准(就是内容纵向填充全屏的页面)做适配的。但是上述方法只能保证一个方向,那我就让它可以自由的切换适配的基准方向不就好了。
最终方案
继续修改之后我得到了最终的方案,修改过后这个类中的所有内容如下:
private static float appDensity; private static float appScaledDensity; private static DisplayMetrics appDisplayMetrics; //此方法在Application的onCreate方法中调用 Density.setDensity(this); public static void setDensity(@NonNull Application application) { //获取application的DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics(); if (appDensity == 0) { //初始化的时候赋值(只在Application里面初始化的时候会调用一次) 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() { } }); } //调用修改density值的方法(默认以宽度作为基准) setAppOrientation(null, AppUtils.WIDTH); } //此方法用于在某一个Activity里面更改适配的方向 Density.setOrientation(mActivity, "width/height"); public static void setOrientation(Activity activity, String orientation) { setAppOrientation(activity, orientation); } /** * targetDensity * targetScaledDensity * targetDensityDpi * 这三个参数是统一修改过后的值 * * orientation:方向值,传入width或height */ private static void setAppOrientation(@Nullable Activity activity, String orientation) { float targetDensity = 0; try { Double division; //根据带入参数选择不同的适配方向 if (orientation.equals("height")) { //appDisplayMetrics.heightPixels/667 division = Operation.division(appDisplayMetrics.heightPixels, 667); } else { division = Operation.division(appDisplayMetrics.widthPixels, 360); } //由于手机的长宽不尽相同,肯定会有除不尽的情况,有失精度,所以在这里把所得结果做了一个保留两位小数的操作 DecimalFormat df = new DecimalFormat("0.00"); String s = df.format(division); targetDensity = Float.parseFloat(s); } catch (NumberFormatException e) { e.printStackTrace(); } float targetScaledDensity = targetDensity * (appScaledDensity / appDensity); int targetDensityDpi = (int) (160 * targetDensity); /** * * ***在这里将修改过后的值赋给系统参数 * * (因为最开始初始化的时候,activity为null,所以只设置application的值就可以了... * 所以在这里判断了一下,如果传有activity的话,再设置Activity的值) */ if (activity != null) { DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics(); activityDisplayMetrics.density = targetDensity; activityDisplayMetrics.scaledDensity = targetScaledDensity; activityDisplayMetrics.densityDpi = targetDensityDpi; } else { appDisplayMetrics.density = targetDensity; appDisplayMetrics.scaledDensity = targetScaledDensity; appDisplayMetrics.densityDpi = targetDensityDpi; } }
这是修改之后的所有内容,不懂的地方可以看一下里面的注释,在里面我是默认的以宽度来作为基准(这是在Activity中设置的方法,存在于此Activity下的fragment,dialog和PopupWindow都会受到此效果的影响,也就是说,在Activity中设置一次之后,Activity下的其他子View都无需再设置一次)。
使用方法
自己创建一个类,将最终方案里面的代码复制粘贴就可以使用了
使用方法:在Application的onCreate()方法中
如果只是适配一个方向的话,只设置这一句就可以了(我在utils里面设置了默认按照宽度适配,可以根据自己的需求修改默认的适配方向,见下图)
若app中有某一个页面需要纵向适配的话:
/** * * 由于是个人封装,此方法需要写在onCreate()中的setContentView()方法前面,切换方向的效果才会生效 */ @Override public void setOrientation() { Density.setOrientation(this, AppUtils.HEIGHT); } /** * * 如果在一个Activity里面切换了适配方向的话,需要在destroy里面将方向设置为默认的方向, * 因为切换方向修改的是Activity的值,但是application的也会覆盖掉(原因还没有搞清楚...), * 权衡利弊之后就在onDestroy这个生命周期里面重新初始化了一下方向(因为用高度作为适配基准的页面 * 少之又少,这样可以***程度的减少对程序功能性的影响) */ @Override protected void onDestroy() { super.onDestroy(); Density.setOrientation(this, AppUtils.WIDTH); }
由于在某一个Activity里面切换方向之后,我修改掉的是Activity中的值(activityDensity),但是返回再点击其他页面之后发现其他页面的适配方向也被修改掉了,于是乎权衡利弊之后我就用了这个相对来说影响最小的办法:在需要修改适配方向的Activity中的onDetroy生命周期里面,再手动将方向改成默认。。。(捣鼓了很久实在是想不到更好的办法了,如果各位看官有其他的好办法可以给我留言)。
***贴出纵向适配的效果图,页面中蓝色背景的TextView高度是固定的150dp(只是我自己写的一个很简单的页面,不要嫌丑。。。):
敲黑板!!!
用此方法写适配,只需要一个dimens文件,一个layout文件就足矣,在xml布局中直接只用dp就可以了(Android P的刘海屏需要单独适配layout,全面屏手机可以隐藏的虚拟按键似乎也需要单独适配。。。)
感谢你能够认真阅读完这篇文章,希望小编分享的“Android屏幕适配的示例分析”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。