androd 今日头条的屏幕适配理解

栏目: Android · 发布时间: 6年前

内容简介:前一段时间无意中看到今日头条的适配方案,使用到项目中,感觉真的是无比丝滑。所以特意写一篇文章分享给小伙伴们!做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是怎么计算的呢?

androd 今日头条的屏幕适配理解

上面图片说明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字体的density
activityDm.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);
//弹出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();
}
复制代码

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


以上所述就是小编给大家介绍的《androd 今日头条的屏幕适配理解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Host Your Web Site In The Cloud

Host Your Web Site In The Cloud

Jeff Barr / SitePoint / 2010-9-28 / USD 39.95

Host Your Web Site On The Cloud is the OFFICIAL step-by-step guide to this revolutionary approach to hosting and managing your websites and applications, authored by Amazon's very own Jeffrey Barr. "H......一起来看看 《Host Your Web Site In The Cloud》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具