android 适配笔记

ps: 适配啊对于 Android 来说永远不会过时

相关概念

屏幕尺寸
  • 含义:手机对角线的物理尺寸
  • 单位:英寸(inch),1英寸=2.54cm

Android手机常见的尺寸有5寸、5.5寸、6寸等等

px 屏幕分辨率
  • 含义:手机在横向、纵向上的像素点数总和
    一般描述成屏幕的"宽x高”=AxB,例如 1080x1920,即宽度方向上有1080个像素点,在高度方向上有1920个像素点
  • 单位:px(pixel),1px=1像素点
    UI设计师的设计图会以px作为统一的计量单位,常见分辨率有:320x480、480x800、720x1280、1080x1920、1080*2160
dp 屏幕像素密度

Android 特有的单位,注意这是像素密度

  • 含义:每英寸的像素点数
  • 单位:dpi(dots per ich)
  • 计算方式
    例如现在有一台手机如下:分辨率 1080*1920,屏幕物理大小 5英寸。先算出对角线的长度=2203,然后除以5=440dp,dp 就是这么算出来的,详细看图:


    android 适配笔记_第1张图片
    944365-5f2509be9276460c.png

    android 适配笔记_第2张图片
    944365-2b5dc928ab334440.png

Android 常用的图片文件夹和 dp 关系看下图:


android 适配笔记_第3张图片
1447577693400_7.png

android 适配的困境

android 的适配是很困难的,为啥,很简单 google 早期预想的 dp 适配体系现在完全没用,要靠自己做适配,这样能不困难吗

Android 碎片化现状:

  • 定制系统奇多
    由于Android系统的开放性,任何用户、开发者、OEM厂商、运营商都可以对Android进行定制,于是出现了小米定制的MIUI、魅族定制的flyme、华为定制的EMUI等等
  • 屏幕尺寸和分辨率组合复杂
    * 屏幕尺寸有 4.0寸,4.2,4.5,4.8,5.0,5.2,5.5,5.8,6.0,6.1,6.2,6.8,7
    * 屏幕分辨率有 320x480、480x800、720x1280、1080x1920,1080*2160

Android 早期规定以 160dp(即屏幕分辨率为320x480)为基准:1dp=1px,然后配合 google 官方建议的上述屏幕分辨率和物理尺寸,同分辨率,物理尺寸下的手机拥有相同的 dp ,可以计算出 dp 和 px 的转换值,然后利用不同 dp values 文件下的 dimens 文件做适配

想象很美好,但是实际手机各方面技术发展很快,完全超过了 Google 的设想,手机厂商也不鸟 google 的建议规范,上面这么多 分辨率和屏幕尺寸的组合,让 手机基本都处于不同的 dp 值状态,这些 dp 值变化没有规律,所以让 dp 适配成为不可能,dp 值太多,无法做到适配

最简单的就是不同的手机基本有不同的 dp 值,宽和高的 dp 都不一样,一个手机 320dp * 480dp ,另一个手机 360dp * 560dp,宽和高的 dp 数不一样,变化没有规则,那怎么能再用 dp 做适配呢


Android 资源与资源限定符

Android 在不同环境下会选择不同的资源,比如在不同的分辨率下,使用不同的图片,Android 的资源体系是 Android 适配的基础也是核心

android 资源种类如下:


android 适配笔记_第4张图片
20160615191144010.png
  • animator
    用于定义属性动画的 XML 文件
  • anim
    定义渐变动画的 XML 文件
  • color
    用于定义颜色状态列表
  • drawable
    位图文件(.png、.9.png、.jpg、.gif)、状态列表、形状...
  • mipmap
    适用于不同启动器图标密度的 mipmap/ 文件夹管理启动器图标的详细信息。
  • layout
    用于定义用户界面布局。
  • menu
    用于定义应用菜单(如选项菜单、上下文菜单或子菜单)的 。
  • raw
    要以原始形式保存的任意文件(2.3版本要求1M)。
  • values
    包含字符串、整型数和颜色等简单值的
  • xml
    可以在运行时通过调用 Resources.getXML() 读取的任意 XML 文件

android 资源限定符如下:


android 适配笔记_第5张图片
Snip20180709_1.png

资源的适配是按照上述图中顺序位为优先级,一步一步塞选出符合条件的文件夹,详细看下图:


android 适配笔记_第6张图片
20160615191159339.png

比如有以下几个适配需要做:

  • 区域设置 = en-GB
  • 屏幕方向 = port
  • 屏幕像素密度 = hdpi
  • 触摸屏类型 = notouch
  • 主要文本输入法 = 12key

然后我们根据上述要求生成相应文件夹,以 drawable 为例:

  • drawable-en/
  • drawable-en-port/
  • drawable-en-notouch-12key/
  • drawable-land-hdpi/
  • drawable-port-notouch-12key/

资源适配文件夹命名规范:

  • 按照优先级写配置
  • 可以支持多级配置,中间以 - 链接,没有空格

除了 drawable 文件夹,res 下面的所有文件夹都进行资源适配

大家都是很聪明的,虽然我写的不是很清晰,但是我想大家都应该能够理解,毕竟这个点不难不是,难在我们没有系统接触过,相关资源也很少

搞清楚了 Android 的资源适配,基本 Android 适配就稳了,android 的适配思路就是就是利用各种工具或是方式,针对不同的条件书写所有的适配资源文件夹,本质永远是资源适配

常见我们使用的是 sw 和 分辨率 2种适配思路:


android 适配笔记_第7张图片
5382223-4f42a5637f53f582.png
android 适配笔记_第8张图片
5382223-c59b8353245a0ebd.png

适配方案一览

常见思路:

  • 传统 dimens 思路
  • layout 布局内适配
  • 根据分辨率提供不同 layout 布局
  • gogole 百分比布局库

来自人民群众的智慧:

  • ScreenAdaptation
  • AutoLayout
  • Rudeness
  • 批处理自动生成 px,dp 的百分比 dimens 文件夹

经典的适配思路

对于经典的适配方式,这里借助鸿神的文章:

  • Android 屏幕适配方案
  • Android 百分比布局库(percent-support-lib) 解析与扩展
  • Android 增强版百分比布局库 为了适配而扩展

第一篇:主要是根据设计图的尺寸,然后将设计图上标识的px尺寸,转化为百分比,为所有的主流屏幕去生成对应百分比的值,每个尺寸都会有一个values文件夹。存在一些问题:产生大量的文件夹,适配不了特殊的尺寸(必须建立默认的文件夹)

第二篇和第三篇:这两篇属于一样的了,主要是基于Google推出的百分比布局,已经很大程度解决了适配的问题。存在一些问题:使用起来比较反人类,因为设计图上标识的都是px,所以需要去计算百分比,然后这个百分比还是依赖父容器的,设计图可能并不会将每个父容器的尺寸都标识出来,所有很难使用(当然,有人已经采用自动化的工具去计算了)。还有个问题就是,因为依赖于父容器,导致ScrollView,ListView等容器内高度无法使用百分比。


传统 dimens 思路

其实这个不用我多说了吧,就是在不同配置环境是中配置 id 相同,但是数值不同的 dimens 参数,已实现适配。早先 google 建议我们在 android 的 m,h,l,x,xx,xxx values 文件夹中去配 dimens 参数,但是现在这个好使了, 这几种 dp 的配置早就不嗯呢乖覆盖所有的 android 设备了。最近几年出现了很多 dimens 思路的变种,思路和传统 dimens 一样,区别在于可以适配更多的设备


layout 布局内适配

这个思路大家都做过,就是利用不同布局自身的特殊属性来动态是适配屏幕的变化,本质上利用布局 viewGroup 的 onMeasu,onLayout 动态计算来实现

我们常见的布局有下面几种:

  • 帧布局(FrameLayout)
  • 绝对布局(AbsoluteLayout)
  • 线性布局(Linearlayout)
  • 相对布局(RelativeLayout)
  • 约束布局 (ConstraintLayout)

常见的手段比如:

  • 使用 Linearlayout 的 weight 比重保持控件之间的宽或高关系
  • 使用 RelativeLayout 的相对位置排版
  • 使用 ConstraintLayout 的 view 宽高比例,chain view 链,比重,辅助线

这些手段只是实现的 layout 页面级别的一些适配,但是不是整个 andoid 的基础,另外使用这些适配手段会造成 viewGroup 对自身的对此计算,布局,明显加重布局显示任务量,会造成页面卡顿

对于这些手段来说,我们不要过多依赖,想还是要继续探索 android 适配更合理的方式


AutoLayout

鸿神开源的适配库,使用很简单,但是可惜不更新了,另外有人反应多少有点小问题,同时该库也存在扩展性较差。对于每一种ViewGroup都要对应编写对应的AutoLayout进行扩展,对于各View的每个需要适配的属性都要编写代码进行适配扩展;在onMeasure阶段进行数值计算。消耗性能,并且这对于非LayoutParams中的属性存在较多不合理之处。

这个适配方案是需要我们 activity 继承 AutoLayoutActivity 即可,然后在 xml 中使用 UI 图中的 px 值就可以了,然后我们就可以根据下面这张最常见的 UI 图用 px 做适配了


android 适配笔记_第9张图片
20151123092142493.jpeg

在 app 项目中的 AndroidManifest 中注明注册设计图尺寸即可



详细的去看鸿神的文章:

  • Android AutoLayout全新的适配方式 堪称适配终结者

ScreenAdaptation

这是上一个朋友的思路,核心就是重置屏幕密度。UI 给一个模板图,使用 dp 单位,然后我们根据这个标准图的分辨率和屏幕密度为标准,就算和安装机器的缩放比例,然后修改安装机器的屏幕参数以达到适配的目的。核心代码如下:

/**
     * 重置屏幕密度
     */
    public static void resetDensity(Context context) {
        //绘制页面时参照的设计图尺寸
        final float DESIGN_WIDTH = 800f;
        final float DESIGN_HEIGHT = 1280f;
        final float DESTGN_INCH = 5.0f;
        //大屏调节因子,范围0~1,因屏幕同比例放大视图显示非常傻大憨,用于调节感官度
        final float BIG_SCREEN_FACTOR = 0.8f;

        DisplayMetrics dm = context.getResources().getDisplayMetrics();

        //确定放大缩小比率
        float rate = Math.min(dm.widthPixels, dm.heightPixels) / Math.min(DESIGN_WIDTH, DESIGN_HEIGHT);
        //确定参照屏幕密度比率
        float referenceDensity = (float) Math.sqrt(DESIGN_WIDTH * DESIGN_WIDTH + DESIGN_HEIGHT * DESIGN_HEIGHT) / DESTGN_INCH / DisplayMetrics.DENSITY_DEFAULT;
        //确定最终屏幕密度比率
        float relativeDensity = referenceDensity * rate;

        if (ORIGINAL_DENSITY == -1) {
            ORIGINAL_DENSITY = dm.density;
        }
        if (relativeDensity > ORIGINAL_DENSITY) {
            relativeDensity = ORIGINAL_DENSITY + (relativeDensity - ORIGINAL_DENSITY) * BIG_SCREEN_FACTOR;
        }
        dm.density = relativeDensity;
        dm.densityDpi = (int) (relativeDensity * DisplayMetrics.DENSITY_DEFAULT);
        dm.scaledDensity = relativeDensity;
    }

详细的请看作者的文章:

  • Android开发一步到位屏幕适配解决方案

试了试 demo 的效果,demo 用的是 dp ,不同设别还原还可以,但是对于屏幕比例变动的就不行了,会产生拉伸或压缩的问题。

虽说我们在 xml 中写的是 dp,但是系统在渲染时还是会最终转换成 px 的,所以就是利用这个思路

举个例子:

  • 比如有 2个设备 A / B,像素密度 A 160 dp ,1dp = 1px ;B 480 dp ,1dp = 3px。
  • 有一个 view 占屏幕大小的 1/4 ,那么 1/4 在 A / B 分别有多大呢
  • 在 B 中 1/4 = 240 dp * 240 dp
  • 在 A 中 1/4 = 80 dp * 80 dp
  • 那么保持占用体积比例不变,B 的 view 宽高需要缩小 3 倍 ,这个 3 就是 A 和 B 之间的像素密度倍数
  • 那么就可以推导出,像素密度高的 view 需要缩小像素密度倍数才能在保持 view 在低像素密度时 view 占用空间比例不变
  • 但是现在使用的高像素密度设备 view 的宽高值,那么为了保证 Android 系统最后计算出的 px 值正确,我们只能等比例缩小所在设备的 displayMetrics.density 和 displayMetrics.densityDpi

上面的这个开源库,别看写的麻烦, 基本就是这个思路


Rudeness

也是一种百分比的思路,和 ScreenAdaptation 的思路一样,但是实现不一样,是以宽为基准匹配,不考虑高的因素,借助了 pt 这个单位,利用的是 android 系统中对于不同单位的换算公式,在其中插入百分数以实现动态计算百分比对应的 px 值的目的

系统单位转换公式: TypedValue#applyDimension(int unit, float value, DisplayMetrics metrics)

public static float applyDimension(int unit, float value,
                                       DisplayMetrics metrics)
    {
        switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }

我们只要在借助系统的单位换算公式,使用里面的某个参数可以实现注入我们需要的计算逻辑的公式。我们选择 xdpi 这个参数注入逻辑,designWidth 是 UI 图宽 px 值

Point size = new Point();
activity.getWindowManager().getDefaultDisplay().getSize(size);
resources.displayMetrics.xdpi = screenPoint.x / autoWidth * 72f

我们借助 pt 这个单位,然后修改 xdpi 这个参数,在期中除以 designWidth ,就能间接的实现百分比计算了。

详细请这里:

  • 一种粗暴快速的Android全屏幕适配方案

关于 pt 适配思路,我之后又研读一次,自己写了工具出来,有兴趣可以看:

  • 屏幕适配 - pt 思路

批处理自动生成 px,dp 的百分比 dimens 文件夹

这是我上个公司以为前辈的方案,根据 UI 图出算对应的所有百分比在不同的 dp 环境下 values 中 demens 中对应的 dp 值,然后我们引用。

这个方案得到的值不是很精确,但是基本可以满足需求,没有上面2个开源方案面对的系统不知道什么时候把显示矩阵参数还原的问题。

示意图:


android 适配笔记_第10张图片
Snip20180712_2.png

这是前辈写的 java 类,也是以宽为基准,编辑 java 文件填写相应的 UI 图分辨率,然后运行就会输出相应的所有 values 文件夹,放到项目中就可以用了。时间有点长了,这个 java 类我找找,找到我会放上来的

后来我发现早就有人写了这个方案,分为 px 适配和 dp 适配,px 的思路和我前辈的一样,dp 的需要自己算 UI 标准 dp 才行

px 适配看:

  • 批处理出 px,dp 的百分比 dimens 文件夹
  • Android dp方式的屏幕适配工具使用(bat批处理方式)

全面屏适配

部分黑屏处理

全面屏适配为啥还要适配,因为全面屏的宽高比比较高,一般是 18:9 ,超过系统默认值

Android 配置文件里 application 中有一个元素 max_aspect ,这是 Android 系统支持的最大宽高比,默认是 1.86 = 16:9,超过这个数,屏幕的高就有部分会黑屏无法利用了,下面是个例子:


android 适配笔记_第11张图片
Snip20180706_26.png

好在,可以设置这个参数

  

max_aspect 设置为 2.1 = 18.5:9 ,基本就能应对现在的全面屏手机的屏幕宽高比了,不够还可以往上加

之后我看到了,在配置文件中设置 resizeableActivity = true 就可以不用 maxAspectRatio 了,貌似现在都有 19.5:9 的屏幕了,2.1不够了,大家设置的大一些

  
android 适配笔记_第12张图片

application maxAspectRatio 属性 26 以上才支持,所以保险起见,大家还是在 配置文件里加 mate-data 吧



启动页处理

全名平带来了新的屏幕宽高比 - 18:9,在启动页时,我们都是全屏显示的,新的分辨率要求启动页的背景图适应新的宽高比


android 适配笔记_第13张图片
cmOLumrNib1fIKemR6pK1gNB1Sgg6Ct0aicK1viaUVCshwGWUbuzhm3ib6ibXFyDIVTx2QK2uY2410VrdSqNvRPFU7g.jpeg

处理思路无外乎条件系统的资源文件夹和 .9 适配了,不过 .9适配需要 UI 作图时考虑给我们留边,要不我们也没放拉伸,新的图片文件夹这样建 - drawable-xxhdpi-2160x1080 、drawable-long


刘海屏适配

国内的刘海屏时刻在太坑了,国内厂商早在 google 之前就用上了 刘海屏 ,所以 google 在 android p 28 上提供的方法就别想用了啦,没戏,我们之只能挨个商家去适配...

这方面有非常好的文章,我就不复制了,大家直接看下面的资料把

google P 适配思路:

  • Android 刘海屏适配全攻略

华为,小米,oppo,vivo 适配四思路

  • Android 刘海屏适配总结

参考资料:

  • Android Res资源适配详解
  • Android 屏幕适配:最全面的解决方案
  • Android开发一步到位屏幕适配解决方案
  • 一种粗暴快速的Android全屏幕适配方案

你可能感兴趣的:(android 适配笔记)