前一段时间无意中看到今日头条的适配方案,使用到项目中,感觉真的是无比丝滑。所以特意写一篇文章分享给小伙伴们!

本文知识点:

  • 为什么要做屏幕适配
  • 今日头条的适配方案(划重点)
  • 今日头条的适配方案的一些问题

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为基准的适配,当然这个值你是可以修改成任何尺寸的!

  1. 先计算一下屏幕的宽度
//这里widthPixels代表屏幕的宽度activityDm.density = activityDm.widthPixels / 360;复制代码
  1. 计算一下字体的density
//这里通过一个比例确定activity字体的densityactivityDm.scaledDensity = activityDm.density * (systemDm.scaledDensity / systemDm.density);复制代码
  1. 计算相应的dpi
//上面有相应的公式activityDm.densityDpi = (int) (160 * activityDm.density);复制代码
  1. 复制相应的内容
//进行相应的赋值操作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);//弹出ToastToast.makeText(this, "点击了第一个内容", Toast.LENGTH_SHORT).show();//重新适配ScreenUtils.restoreVerticalAdaptScreen(this, 720);复制代码

像什么Toast、dialog什么的都会出现上面的情况,所以解决办法是一样的

3.2 webview加载后发现density复原

由于 WebView 初始化的时候会还原 density 的值导致适配失效,继承 WebView,重写如下方法:

@Overridepublic void setOverScrollMode(int mode) {    super.setOverScrollMode(mode);    ScreenUtils.restoreAdaptScreen();}复制代码

特别感谢: blankj的Android 屏幕适配从未如斯简单

更多相关文章

  1. 转:APK Crack
  2. Android让Fragment加载到Activity中
  3. Android(安卓)代码混淆机制
  4. 使用库项目实现Android程序代码的复用
  5. Android(安卓)动态设置TextView的drawableLeft等属性
  6. Android(安卓)Studio导入jar后无法识别、但项目能正常运行的问题
  7. Android之——使用Android(安卓)studio创建的AIDL编译时找不到自
  8. android中使用代码混淆出现Conversion to Dalvik format failed
  9. Android(安卓)闪电效果 (Electric Screen,电动屏幕)

随机推荐

  1. Android面试(9): Activity的四种启动模式
  2. Android(安卓)Studio发布到Jcenter
  3. Android:长按连续响应的Button
  4. Android(安卓)SDK环境配置
  5. Android数据存储方式之:ContentProvider
  6. Day2.3--Android常用布局之RelativeLayou
  7. 自定义Logcat,完全控制打印信息
  8. JNI 引用问题梳理
  9. Android(安卓)vector 标签 pathData
  10. Android(安卓)ContentProvider的实现及简