内容简介:前一段时间无意中看到今日头条的适配方案,使用到项目中,感觉真的是无比丝滑。所以特意写一篇文章分享给小伙伴们!做Android开发的都了解,由于Android屏幕碎片化严重,虽然Android官方提供了dp为单位的适配方案,但是由于各种千奇百怪的机型,所以变现往往不尽如人意。所以需要进行屏幕适配。说白了就是让所有机型都进行保持UI的设计原貌!终于到了本文的重点了。为了大家能深刻理解其中的含义,这里从最基本的开始说起。
前一段时间无意中看到今日头条的适配方案,使用到项目中,感觉真的是无比丝滑。所以特意写一篇文章分享给小伙伴们!
本文知识点:
- 为什么要做屏幕适配
- 今日头条的适配方案(划重点)
- 今日头条的适配方案的一些问题
1. 为什么要做屏幕适配
做Android开发的都了解,由于Android屏幕碎片化严重,虽然Android官方提供了dp为单位的适配方案,但是由于各种千奇百怪的机型,所以变现往往不尽如人意。所以需要进行屏幕适配。说白了就是让所有机型都进行保持UI的设计原貌!
2. 今日头条的适配方案
终于到了本文的重点了。为了大家能深刻理解其中的含义,这里从最基本的开始说起。
2.1 传统的dp适配的流程
android中的dp在渲染前会将dp转为px,计算公式:
px = density * dp; density = dpi / 160; px = dp * (dpi / 160); 复制代码
而dpi是根据屏幕真实的分辨率和尺寸来计算的,每个设备都可能不一样的。那么dpi是怎么计算的呢?
上面图片说明dpi是怎么计算得来的。举个例子,当屏幕分辨率为1920 * 1080屏幕尺寸为5寸的手机。计算得来的dpi为440。不信的话可以计算一下!
那么问题来了?
假设我们UI设计图是按屏幕宽度为360dp来设计的,那么在上述设备上,屏幕宽度其实为1080/(440/160)=392.7dp,也就是屏幕是比设计图要宽的。这种情况下, 即使使用dp也是无法在不同设备上显示为同样效果的。 同时还存在部分设备屏幕宽度不足360dp,这时就会导致按360dp宽度来开发实际显示不全的情况。
而且上述屏幕尺寸、分辨率和像素密度的关系,很多设备并没有按此规则来实现, 因此dpi的值非常乱,没有规律可循,从而导致使用dp适配效果差强人意。
3.2 今日头条的适配方式说明
其实,当我们拿到设计图的时候,一般都是根据苹果的6进行设计的,往往在Android中,存在16:9和4:3的一些机型,那么这些机型中的宽高比不同,如果想完全按照设计图进行适配是不可能的,也是不现实的。但是如果我们以一个维度,也就是宽这个维度来进行适配的话,如果高度超出了屏幕我们就使用可滑动的控件进行展示。这就是今日头条的适配方案。
因此,采用以宽度为标准去进行适配,保持该维度上和设计图一致
2.3 今日头条的适配方案
先科普几个内容,
- dp和px的转换公式为:px = dp * density
- dp转换的场景都是通过DisplayMetrics来进行计算的,
- DisplayMetrics#density 就是上述的density
- DisplayMetrics#densityDpi 就是上述的dpi
- DisplayMetrics#scaledDensity 字体的缩放因子,正常情况下和density相等,但是调节系统字体大小后会改变这个值
因为所有关于dp的计算都是通过DisplayMetrics这个类进行的。所以只需要针对这个类进行操作就可以了。
我简单把DisplayMetrics类分为三个层面,第一个是System(可以理解成初始分配)的,第二个是APP(可以理解成Application)的,第三个是Activity的。当你适配的时候,尽量不要去修改第一个System中的Displaymetris的,因为可能第三方的库不会按照你的方式去适配,所以这里只修改后面两个就可以了。第一个不修改是便于之后的还原!!!
以下是三个层面获取DisplayMetrics中的代码:
// 系统的屏幕尺寸 final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics(); // app整体的屏幕尺寸 final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics(); // activity的屏幕尺寸 final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics(); 复制代码
接下来我们看看需要怎么适配,这里就只以屏幕宽度为基准进行相应的适配了。这里模拟360dp为基准的适配,当然这个值你是可以修改成任何尺寸的!
- 先计算一下屏幕的宽度
//这里widthPixels代表屏幕的宽度 activityDm.density = activityDm.widthPixels / 360; 复制代码
- 计算一下字体的density
//这里通过一个比例确定activity字体的density activityDm.scaledDensity = activityDm.density * (systemDm.scaledDensity / systemDm.density); 复制代码
- 计算相应的dpi
//上面有相应的公式 activityDm.densityDpi = (int) (160 * activityDm.density); 复制代码
- 复制相应的内容
//进行相应的赋值操作 appDm.density = activityDm.density; appDm.scaledDensity = activityDm.scaledDensity; appDm.densityDpi = activityDm.densityDpi; 复制代码
整体代码如下:
/** * 适配的主要代码 * * @param activity 上下文 * @param sizeInPx 你要适配的相应尺寸 * @param isVerticalSlide 水平还是垂直为参考 */ private static void adaptScreen(final Activity activity, final int sizeInPx, final boolean isVerticalSlide) { // 系统的屏幕尺寸 final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics(); // app整体的屏幕尺寸 final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics(); // activity的屏幕尺寸 final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics(); if (isVerticalSlide) { activityDm.density = activityDm.widthPixels / (float) sizeInPx; Log.e(TAG, "adaptScreen: "+activityDm.widthPixels ); } else { activityDm.density = activityDm.heightPixels / (float) sizeInPx; } // 字体的缩放因子,这个是通过一个比例计算得来的! activityDm.scaledDensity = activityDm.density * (systemDm.scaledDensity / systemDm.density); // 计算得到相应的dpi activityDm.densityDpi = (int) (160 * activityDm.density); //进行相应的赋值操作 appDm.density = activityDm.density; appDm.scaledDensity = activityDm.scaledDensity; appDm.densityDpi = activityDm.densityDpi; } 复制代码
因为上面涉及到横竖屏的问题,所以这里有个if判断。上面是主要代码。
3 今日头条的适配方案的一些问题
3.1 适配之后Toast的问题?
进行上面的适配之后,Toast会变得很小。其实也不难理解,因为你修改了APP的density,所以整个图片的界面都会发生相应的变化也就很好理解了。那么怎么解决呢?其实就想上面说的,使用System的density对App和Activity进行还原。怎么说呢?其实就是在show()方法之前还原,在之后在进行适配。
怎么取消呢?看下面的代码。
public static void cancelAdaptScreen(final Activity activity) { final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics(); final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics(); final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics(); activityDm.density = systemDm.density; activityDm.scaledDensity = systemDm.scaledDensity; activityDm.densityDpi = systemDm.densityDpi; appDm.density = systemDm.density; appDm.scaledDensity = systemDm.scaledDensity; appDm.densityDpi = systemDm.densityDpi; } 复制代码
其实就是使用System的density把APP和Activity的density修改回来就可以了!
然后在show()方法之后使用下面方法重新对界面进行适配!
public static void restoreAdaptScreen(Activity activity, boolean isVerticalSlide, int sizeInPx) { final DisplayMetrics systemDm = Resources.getSystem().getDisplayMetrics(); final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics(); final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics(); if (isVerticalSlide) { activityDm.density = activityDm.widthPixels / (float) sizeInPx; } else { activityDm.density = activityDm.heightPixels / (float) sizeInPx; } activityDm.scaledDensity = activityDm.density * (systemDm.scaledDensity / systemDm.density); activityDm.densityDpi = (int) (160 * activityDm.density); appDm.density = activityDm.density; appDm.scaledDensity = activityDm.scaledDensity; appDm.densityDpi = activityDm.densityDpi; } 复制代码
调用代码就变成了这个样子
//取消适配 ScreenUtils.cancelAdaptScreen(this); //弹出Toast Toast.makeText(this, "点击了第一个内容", Toast.LENGTH_SHORT).show(); //重新适配 ScreenUtils.restoreVerticalAdaptScreen(this, 720); 复制代码
像什么Toast、dialog什么的都会出现上面的情况,所以解决办法是一样的
3.2 webview加载后发现density复原
由于 WebView 初始化的时候会还原 density 的值导致适配失效,继承 WebView,重写如下方法:
@Override public void setOverScrollMode(int mode) { super.setOverScrollMode(mode); ScreenUtils.restoreAdaptScreen(); } 复制代码
以上所述就是小编给大家介绍的《androd 今日头条的屏幕适配理解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Collective Intelligence实战
阿拉克 / 2010-9 / 58.00元
《Collective Intelligence实战》内容简介:在互联网上,利用用户的集体智慧是成功的关键。集体智慧是一种新兴的编程技术,可让您从人们访问web和与web交互的过程中找到有价值的模式、发现这些访问者之间的关系和确定他们的个人偏好及习惯等。《collective Intelligence实战》首先介绍了集体智慧的原则和构建更具交互性网站的思想,然后通过示例开发了一个直接可用的基于Ja......一起来看看 《Collective Intelligence实战》 这本书的介绍吧!