Android 目前最稳定和高效的UI适配方案

作者: 拉丁吴 | 来源:发表于2018-06-19 00:55 被阅读3次

Android系统发布十多年以来,关于Android的UI的适配一直是开发环节中最重要的问题,但是我看到还是有很多小伙伴对Android适配方案不了解。刚好,近期准备对糗事百科Android客户端设计一套UI尺寸适配方案,可以和小伙伴们详细的聊一聊这个问题。

Android适配最核心的问题有两个,其一,就是适配的效率,即把设计图转化为App界面的过程是否高效,其二如何保证实现UI界面在不同尺寸和分辨率的手机中UI的一致性。这两个问题都很重要,一个是保证我们开发的高效,一个是保证我们适配的成效;今天我们就这两个核心的问题来聊一聊Android的适配方案。

image

首先,大家都知道,在标识尺寸的时候,Android并不推荐我们使用px这个真实像素单位,因为不同的手机之间,分辨率是不同的,比如一个96*96像素的控件在分辨率越来越高的手机上会在整体UI中看起来越来越小。

image

出现类似于上图这样这样,整体的布局效果可能会变形,所以px这个单位在布局文件中是不推荐的。

dp直接适配

针对这种情况,Android推荐使用dp作为尺寸单位来适配UI.

那么什么是dp?dp指的是设备独立像素,以dp为尺寸单位的控件,在不同分辨率和尺寸的手机上代表了不同的真实像素,比如在分辨率较低的手机中,可能1dp=1px,而在分辨率较高的手机中,可能1dp=2px,这样的话,一个96*96dp的控件,在不同的手机中就能表现出差不多的大小了。那么这个dp是如何计算的呢? 我们都知道一个公式: px = dp(dpi/160) 系统都是通过这个来判断px和dp的数学关系,

那么这里又出现了一个问题,dpi是什么呢?

dpi是像素密度,指的是在系统软件上指定的单位尺寸的像素数量,它往往是写在系统出厂配置文件的一个固定值。

我为什么要强调它是软件系统上的概念?因为大家买手机的时候,往往会听到另一个叫ppi的参数,这个在手机屏幕中指的也是像素密度,但是这个是物理上的概念,它是客观存在的不会改变。dpi是软件参考了物理像素密度后,人为指定的一个值,这样保证了某一个区间内的物理像素密度在软件上都使用同一个值。这样会有利于我们的UI适配。

比如,几部相同分辨率不同尺寸的手机的ppi可能分别是是430,440,450,那么在Android系统中,可能dpi会全部指定为480.这样的话,dpi/160就会是一个相对固定的数值,这样就能保证相同分辨率下不同尺寸的手机表现一致。

... 1080*720 1920*1080
dpi 320 480
dpi/160 2 3

而在不同分辨率下,dpi将会不同,比如:

... 1080*720 1920*1080
dpi 320 480
dpi/160 2 3

根据上面的表格,我们可以发现,720P,和1080P的手机,dpi是不同的,这也就意味着,不同的分辨率中,1dp对应不同数量的px(720P中,1dp=2px,1080P中1dp=3px),这就实现了,当我们使用dp来定义一个控件大小的时候,他在不同的手机里表现出相应大小的像素值。

image

我们可以说,通过dp加上自适应布局和weight比例布局可以基本解决不同手机上适配的问题,这基本是最原始的Android适配方案。

这种方式存在两个小问题,第一,这只能保证我们写出来的界面适配绝大部分手机,部分手机仍然需要单独适配,为什么dp只解决了90%的适配问题,因为并不是所有的1080P的手机dpi都是480,比如Google 的Pixel2(1920*1080)的dpi是420,也就是说,在Pixel2中,1dp=2.625px,这样会导致相同分辨率的手机中,这样,一个100dp*100dp的控件,在一般的1080P手机上,可能都是300px,而Pixel 2 中 ,就只有262.5px,这样控件的实际大小会有所不同。

为了更形象的展示,假设我们在布局文件中把一个ImageView的宽度设置为360dp,那么在下面两张图中表现是不一样的:

图一是1080P,480dpi的手机,图二是1080P,420dpi的手机

1080P,480dpi的手机
1080P,420dpi的手机

从上面的布局中可以看到,同样是1080P的手机,差异是比较明显的。在这种情况下,我们的UI可能需要做一些微调甚至单独适配。

第二个问题,这种方式无法快速高效的把设计师的设计稿实现到布局代码中,通过dp直接适配,我们只能让UI基本适配不同的手机,但是在设计图和UI代码之间的鸿沟,dp是无法解决的,因为dp不是真实像素。而且,设计稿的宽高往往和Android的手机真实宽高差别极大,以我们的设计稿为例,设计稿的宽高是375px*750px,而真实手机可能普遍是1080*1920,

那么在日常开发中我们是怎么跨过这个鸿沟的呢?基本都是通过百分比啊,或者通过估算,或者设定一个规范值等等。总之,当我们拿到设计稿的时候,设计稿的ImageView是128px*128px,当我们在编写layout文件的时候,却不能直接写成128dp*128dp。在把设计稿向UI代码转换的过程中,我们需要耗费相当的精力去转换尺寸,这会极大的降低我们的生产力,拉低开发效率。

宽高限定符适配

为了高效的实现UI开发,出现了新的适配方案,我把它称作宽高限定符适配。简单说,就是穷举市面上所有的Android手机的宽高像素值:

image

设定一个基准的分辨率,其他分辨率都根据这个基准分辨率来计算,在不同的尺寸文件夹内部,根据该尺寸编写对应的dimens文件。

比如以480x320为基准分辨率

  • 宽度为320,将任何分辨率的宽度整分为320份,取值为x1-x320
  • 高度为480,将任何分辨率的高度整分为480份,取值为y1-y480

那么对于800*480的分辨率的dimens文件来说,

x1=(480/320)*1=1.5px

x2=(480/320)*2=3px

...

image

这个时候,如果我们的UI设计界面使用的就是基准分辨率,那么我们就可以按照设计稿上的尺寸填写相对应的dimens引用了,而当APP运行在不同分辨率的手机中时,这些系统会根据这些dimens引用去该分辨率的文件夹下面寻找对应的值。这样基本解决了我们的适配问题,而且极大的提升了我们UI开发的效率,

但是这个方案有一个致命的缺陷,那就是需要精准命中才能适配,比如1920x1080的手机就一定要找到1920x1080的限定符,否则就只能用统一的默认的dimens文件了。而使用默认的尺寸的话,UI就很可能变形,简单说,就是容错机制很差。

不过这个方案有一些团队用过,我们可以认为它是一个比较成熟有效的方案了。

UI适配框架(已经停止维护)

鸿洋大佬的适配方案的项目也来自于宽高限定符方案的启发。

使用方法也很简单:

第一步:
在你的项目的AndroidManifest中注明你的设计稿的尺寸。

<meta-data android:name="design_width" android:value="768">
</meta-data>
<meta-data android:name="design_height" android:value="1280">
</meta-data>

第二步:
让你的Activity继承自AutoLayoutActivity.

然后我们就可以直接在布局文件里面使用具体的像素值了,比如,设计稿上是96*96,那么我们可以直接写96px,APP运行时,框架会帮助我们根据不同手机的具体尺寸按比例伸缩。

这可以说是一个极好的方案,因为它在宽高限定符适配的基础上更进一步,并且解决了容错机制的问题,可以说完美的达成了开发高效和适配精准的两个要求。

但是我们能够想到,因为框架要在运行时会在onMeasure里面做变换,我们自定义的控件可能会被影响或限制,可能有些特定的控件,需要单独适配,这里面可能存在的暗坑是不可预见的,还有一个比较重要的问题,那就是整个适配工作是有框架完成的,而不是系统完成的,一旦使用这个框架,未来一旦遇到很难解决的问题,替换起来是非常麻烦的,而且项目一旦停止维护,后续的升级就只能靠你自己了,这种代价团队能否承受?当然,它已经停止维护了。

不过仅仅就技术方案而言,不可否认,这是一个很好的开源项目。

小结

讨论的上述几种适配方案都是可以实际用于开发中的比较成熟的方案,而且确实有很多开发者正在使用。不过由于他们各自都存在一些缺陷,所以我们使用了上述方案后还需要花费额外的精力着手解决这些可能存在的缺陷。

那么,是否存在一种相对比较完美,没有明显的缺陷的方案呢?

smallestWidth适配

smallestWidth适配,或者叫sw限定符适配。指的是Android会识别屏幕可用高度和宽度的最小尺寸的dp值(其实就是手机的宽度值),然后根据识别到的结果去资源文件中寻找对应限定符的文件夹下的资源文件。

这种机制和上文提到的宽高限定符适配原理上是一样的,都是系统通过特定的规则来选择对应的文件。

举个例子,小米5的dpi是480,横向像素是1080px,根据px=dp(dpi/160),横向的dp值是1080/(480/160),也就是360dp,系统就会去寻找是否存在value-sw360dp的文件夹以及对应的资源文件。

image

smallestWidth限定符适配和宽高限定符适配最大的区别在于,前者有很好的容错机制,如果没有value-sw360dp文件夹,系统会向下寻找,比如离360dp最近的只有value-sw350dp,那么Android就会选择value-sw350dp文件夹下面的资源文件。这个特性就完美的解决了上文提到的宽高限定符的容错问题。

这套方案是上述几种方案中最接近完美的方案。
首先,从开发效率上,它不逊色于上述任意一种方案。根据固定的放缩比例,我们基本可以按照UI设计的尺寸不假思索的填写对应的dimens引用。
我们还有以375个像素宽度的设计稿为例,在values-sw360dp文件夹下的diemns文件应该怎么编写呢?这个文件夹下,意味着手机的最小宽度的dp值是360,我们把360dp等分成375等份,每一个设计稿中的像素,大概代表smallestWidth值为360dp的手机中的0.96dp,那么接下来的事情就很简单了,假如设计稿上出现了一个10px*10px的ImageView,那么,我们就可以不假思索的在layout文件中写下对应的尺寸。

image

而这种diemns引用,在不同的values-sw<N>dp文件夹下的数值是不同的,比如values-sw360dp和values-sw400dp,

image image

当系统识别到手机的smallestWidth值时,就会自动去寻找和目标数据最近的资源文件的尺寸。

其次,从稳定性上,它也优于上述方案。原生的dp适配可能会碰到Pixel 2这种有些特别的手机需要单独适配,但是在smallestWidth适配中,通过计算Pixel 2手机的的smallestWidth的值是411,我们只需要生成一个values-sw411dp(或者取整生成values-sw410dp也没问题)就能解决问题。

smallestWidth的适配机制由系统保证,我们只需要针对这套规则生成对应的资源文件即可,不会出现什么难以解决的问题,也根本不会影响我们的业务逻辑代码,而且只要我们生成的资源文件分布合理,,即使对应的smallestWidth值没有找到完全对应的资源文件,它也能向下兼容,寻找最接近的资源文件。

当然,smallestWidth适配方案有一个小问题,那就是它是在Android 3.2 以后引入的,Google的本意是用它来适配平板的布局文件(但是实际上显然用于diemns适配的效果更好),不过目前所有的项目应该最低支持版本应该都是4.0了(糗事百科这么老的项目最低都是4.0哦),所以,这问题其实也不重要了。

福利赠送

生成diemns文件的过程以及数据计算方法上面已经讲清楚了,大家完全可以自己去生成这些文件,我在这里附赠生成values-sw<N>的项目代码,大家直接拿去用,是Java工程。点击这里获取项目地址

相关文章

网友评论

  • 雷华杰:Java 工程中 MakeUtils 中MAX_SIZE = 720; 这个有什么说明吗
    拉丁吴:@雷华杰 这个单位应该不是px吧,你用默认的就行了
    雷华杰:@拉丁吴 这个尺寸是单位px,那设置为多大合适
    拉丁吴:@雷华杰 生成dimen文件里面的最大尺寸,
  • 5b4187ae8813:宽高限定符适配 这个方案据说在华为更新系统后会适配不准确,目前没找到原因,而且华为手机可以修改手 分辨率,我这也没有华为的测试机,目前没解决这个问题,打算换成今日头条-适配方案了
    拉丁吴:@30116102 github上提的那个问题?我这边没有发现华为手机存在这样的问题。不能确定这个问题到底是不是存在,其实8.0以上的手机开发者模式都能修改宽度dp值,这是测试用的。
  • ender115:大神,我用smallestWidth适配方案,然后设计稿给出的宽度是250dp,市面上手机应该没有这个尺寸的,是不是我只需要根据250dp生成300-450之间的values文件就可以了,默认的values文件夹下不用生成dimens.xml文件了吧
    拉丁吴:@ender115 一般icon大小可以直接用dimens里的数据
    ender115:@拉丁吴 那么本地图片呢?是用wrap_content还是用dimens里面的数据比较好
    拉丁吴:@ender115 是的。
  • dcf87a675c8c:1920*1080跟1280*720都会访问sw360dp资源文件夹吗
    拉丁吴:@J_Fooo 看计算出来的宽度dp值是不是360哦,也有可能是390,或者411
  • Yinll:请问这个怎么做平板适配
    拉丁吴:@YinL 嗯,如果你们没有专门对平板设计ui,那么用480被动适配是没问题的,其实450也行,
    Yinll:上方提到了可以直接用480的 这个是否ok
    拉丁吴:@YinL 一样的,平板的宽度dp一般在600-900之间,而且集中某几个数据上,关键设计需要针对平板有专门的设计图
  • 拌饭:您好拉丁吴老师,咱们这个适配TV吗?1920*1080分辨率
    拉丁吴:嗯,我重新更新了GitHub上的项目代码,以后就不会造成这种设计稿最小宽度的误差了
  • 碧海鱼龙:问下大佬,当ppi可能分别是是430,440,450时,将dpi人为规定为480,屏幕的像素点少于规定的像素点,这样不会影响显示效果吗?如果不会,为什么呢!
    _He先森_:应该不会,你的设备dpi是固定的,缩放比例也就定了,系统会默认找相近的dpi文件夹里的图片,然后根据比例缩放,
  • 小学留了三年:看完了 谢谢吴老大
  • 89172709aaba:这个只是以宽度作为维度,计算出值,如果用在竖直方向设置上,比如说设置marginTop或者marginbottom,会不会不准?
    拉丁吴:@日了狗 准确的,你可以试试。
  • 225b0e3ba000:想问一下,smallestWidth 方案对于手机屏幕横屏,1920 x 1080, 是否也是同样适用?? 文件夹尺寸推荐 哪些 以及 如何计算需要生成的文件夹尺寸?
    拉丁吴:@kevin_xin 横屏还是会以1080为基准,要理解最小宽度这个概念。一般手机宽度dp值不超过450。大多集中在360-411之间
    225b0e3ba000:@拉丁吴 我还是没弄明白.假如手机是 1920 x 1080,然后 应用界面固定为横屏. 那么按照公式 1920 /
    (480 / 160) = 640.那么要做适配的话,是不是生成640 - 790 之间的文件尺寸,790是我按 640 + 150自己得出的. 但是按文中的意思,每台1920 x 1080 的dpi 不一定是480.那我应该如何准确的生成屏幕横屏时的文件呢?
    拉丁吴:@kevin_xin 横屏适用,推荐尺寸,360,390,411,300-450之间。其他问题,文中都有说明吧
  • 小白象码农:试了下 好像在preView上面不显示尺寸效果
    小白象码农:@小白象码农 好了 clean 下就好了 :joy:
    小白象码农:@拉丁吴 有的 用系统的dp也能显示. 用dismens文件下的尺寸就不显示. 但运行之后适配效果是出来了的
    拉丁吴:@小白象码农 我这边是显示的,默认的dimens文件夹下没有对应默认的尺寸?
  • 老西子:我的设计师告诉我,设计稿宽度是1080,这和1080是多宽?
    拉丁吴:@老西子 😓😓你填1080
    老西子:@拉丁吴 设计图的宽高是1080*1920,我不知道这个尺寸怎么填的。
    /**
    * 设计稿尺寸(根据自己设计师的设计稿的宽度填入)
    */
    private static final int DESIGN_WIDTH = 375;
    拉丁吴:@老西子 高度呢?设计稿的宽高不需要问设计师吧,自己看一下设计图的宽高就行了
  • 煎鱼号:简书的UI方案呢?大神知道不
  • e1ddbd1bfac4:这种方法我以前用过一段时间,但是有时会有一些小的误差
    拉丁吴:比如说?
  • JessYan:感谢拉丁吴老师的贡献
    大概明白了这个方案,我看有很多人没理解,以下是我的理解,若有不对请指出

    简单拿今日头条的方案做对比,今日头条的方案, 核心原理在于:
    当前设备总宽度(单位为像素)/ 设计图总宽度(单位为 dp) = density
    density 的意思就是 1 dp 占当前设备多少像素
    假设设计图总宽度为 375 dp, 设备总宽度为 1080 px,根据上面的公式 1080 / 375 = 2.88 (density)
    一个 View 在设计图上的尺寸是 50dp * 50dp,所以在所有总宽度为 1080 px 的设备上,这个 View 的高宽都是 50 * 2.88 = 144 px (根据公式 dp * density = px)

    这个 View 的高宽,在设计图上占总宽度的比例是 (50 / 375 = 0.1333),在实际设备上占实际设备总宽度的比例是 (144 / 1080 = 0.1333),该 View 在设计图和实际设备上的比例是相同,所以完成了等比例缩放,该适配方案有效

    至于上文中提到的某些设备总宽度为 1080 px,但是 dpi 不同的设备,其实这个方案根本没有根据 dpi 求出 density,是根据自己的公式求出的 density, 所以这对今日头条的方案没有影响
    JessYan:如果 SW 方案放弃了使用 SP 的话,也就放弃了适配用户更改系统字体大小的操作
    JessYan:总结:

    在 1920*1080,dpi 为 480 的设备上,设计图上 50 * 50 的 view 最终高宽都为 144 px

    在 1920*1080,dpi 为 420 的设备上,设计图上 50 * 50 的 view 最终高宽都为 144.375 px

    可以看到虽然 dpi 不一样但最后的结果却是差不多的,所以即使在高宽一样,dpi 不一样的设备上 smallestWidth 方案也能完成等比例适配,所以这个方案是可行的

    根据我上面的计算,可以看出两个方案的最终原理其实差别不是太大,都是通过各种 dp,px,dpi 的换算来达到设计图的等比例缩放

    今日头条精确度是最完美的,但有一个问题所有地方都是一刀切,会影响老项目和三方库(可以选择某个 Activity 取消适配来缓解这个问题),但是使用起来非常简单,根本不需要什么成本,侵入性也低

    smallestWidth 的方案精确度稍微低一点,你需要使用了就自行引用 dimens,效果和范围都可控,不会像今日头头条一样一刀切,影响其他不需要的地方,使用上比今日头条的方案投入成本更高,侵入性略高,某些页面要实现以高为基准进行适配,或者项目中一些页面需要以高为基准,一些又要一宽为基准,实现上应该比较复杂

    所以这两个方案都是现阶段比较棒的方案,都可以达到等比例适配的效果,大家需要在投入成本和收益之间做出取舍,选择一个目前最适合自己项目的方案
    JessYan:下面同样来说说 smallestWidth 这个方案
    smallestWidth 方案的核心原理在于:
    当前设备总宽度 (单位为 dp)/ 设计图总宽度 (至于是 dp 还是 px 都不重要) = value (设计图上每个单位占当前设备多少 dp)
    然后根据这个 value 算出对应资源文件的 dimens,然后在项目中引用 dimens,系统会选择最合适的宽度的资源文件下的 dimens

    还是以上面的例子:
    假设设计图总宽度为 375,设备总宽度为 360 dp, 根据上面的公式 360 / 375 = 0.96 (意思是在当前设备上设计图的一个单位等于 0.96 dp,求出的结果也与上文中的截图一致)

    一个 View 在设计图上的尺寸是 50 * 50,使用这个方案我们直接在 layout 中引用 @dimen/qb_px_10,系统会选择 sw360dp 的资源文件,根据公式 50 * 0.96 = 48 dp,在当前设备上这个 View 的高宽都为 48 dp

    然后系统会根据公式 (dp * density = px) 把 dp 换算成 px

    测试设备总宽度为 1080 像素,dpi 为480,根据公式 dpi / 160 = density,480 / 160 = 3,求出 density 等于 3

    48 dp 会被系统换算成 144 px (48 * 3 = 144),这个这个 View 的最终高宽都是 144 px

    这个 View 的高宽,在设计图上占总宽度的比例是 (50 / 375 = 0.1333),在实际设备上占实际设备总宽度的比例是 (144 / 1080 = 0.1333),该 View 在设计图和实际设备上的比例是相同,所以完成了等比例缩放

    但这个方案由于没有自己强制更改 density,所以是系统根据 dpi 来求出 density,然后在根据公式 (dp * density = px) 来将 dp 换算成 px,所以这个方案的效果会受 dpi 影响的

    上文中提到过,同样的 1920*1080 的设备,虽然大部分的 dpi 为 480,但有些设备的 dpi 可能是不一样,比如 Pixel 2 的 dpi 为 420

    上面求出的 144 px 是在 1920*1080,dpi 为 480 的设备上

    现在我们试试高宽同样为 1920*1080,但是 dpi 为 420 的 Pixel 2 手机上是否也能完成等比例缩放

    根据公式 px / (dpi / 160) = dp, 1080 / (420 / 160) = 411.429 dp

    设备总宽度为 411.429 dp,使用 smallestWidth 方案生成 sw411dp 对应的资源文件

    根据公式 411 / 375 = 1.10,在当前 dpi 为 420 的设备上设计图的一个单位等于 1.10 dp

    一个在设计图上 50 * 50 的 View,在 dpi 为 420 的设备上,使用 sw411dp 得到的实际尺寸是 50 * 1.10 = 55 dp

    然后系统会根据公式 dp * density = px,把 dp 换算成 px

    55 * (420 / 160) = 144.375 px,这个 View 的最终高宽都是 144.375 px
  • 泡泡之意境:您好,我把那几个java文件添加进我的项目了,运行了项目并没有生成values-sw
    我不太明白怎么去生成文件,您可以再给我讲解一下嘛?
    拉丁吴:@泡泡之意境 你在Android工程里生成文件导出不太方便,如果你确保正确调用了方法的话,建议改下生成路经,不然很难在手机目录里面找。
    泡泡之意境:@拉丁吴 您好,我是android工程,android studio:disappointed_relieved:
    拉丁吴:@泡泡之意境 是Java工程吧?有没有报错?
  • 绳_:最早我从头条团队开源的适配方案看到的。就两个问题 1. 本地资源图片不能用wrap_content,不然还是图片宽高的px值。2.16:9和18:9和19:9的界面坑定不一样
    拉丁吴:@绳_ 哦哦
    绳_:@拉丁吴 比如现在整个界面误导系统为90dp 。16:9的手机 高度就是160dp 18:9的屏幕就是180dp 19:9的高度就是190dp 也就多显示一点。还是挺好用的字体设置全用dp 肯定不会出错
    拉丁吴:@绳_ 是么,现在这个头条的适配方案还有这个坑么?
  • _He先森_:如果不设定具体的宽高,只使用wrap_content和权重的话,选一种折中的density来设计出图,将图放在对应density的drawable文件夹下,是不是就不考虑特殊分辨率的设备了?
    _He先森_:@拉丁吴 嗯嗯,明白,要适配率更高一些,就要考虑更多的屏幕尺寸。
    拉丁吴:@_He先森_ 一般来说具体的尺寸是不可避免需要用到的,我的原则是,wrap_content这类适配的尽量用这个当需要具体尺寸的时候就用适配的尺寸,当然,你说的这种方式,理论上是没怎么问题的
  • ab7419a1bcbe:sw适配这里写的有一点点问题吧,楼主 ,375像素密度本质上是750的屏幕, 设计图10px,xml中还是需要写成pb_px_5吧,不应该是pb_px_10吧
    拉丁吴:@zlc921022 我讲的是以设计师的设计稿宽度为基准,生成各种适配尺寸。
    ab7419a1bcbe:@拉丁吴 不知道楼主的values里面的demens.xml里面怎么定义的 我的values里面是基于sw360这个标准定义的 ,可能是我这里的问题
    拉丁吴:@zlc921022 在仔细看看,搞懂这里的转化原理,没有你想的那么复杂
  • hoberthe:手机的最小限定尺寸可以计算出来么
    拉丁吴:@hoberthe 可以计算,按照上面的方法,在代码里可以计算
  • Android程序员老鸦:autolayout了解一下
    拉丁吴:泓洋大佬的框架是么?文章里面谈到了,
  • 一夜暴富两夜也行:强迫症,看到图片上的Android 写错了。。。
  • 2b77b307c793:虽然我不懂软件开发和编程,但是读完之后,还是晕晕乎乎地收获了一点-就是软件在不同手机上的用户界面会根据像素的不同而有所改变,因此需要适配方案来对软件进行一个调试 ,让界面在所有手机上都能够显示出差不多的样子,我的理解正确吗?虚心请教,发表下读后感,测试下老师的文章是不是小白级别的😂
  • 331be02e5b8e:现在还用这种方案吗
  • 懒惰的勤奋:楼主最后说头条的适配方案对老项目不太适用,可是之前说的smallestWidth适配的原理如果我没有理解错的话也是修改最终的px呀,只不过是间接修改而已哈。结局一应是一样的哈,个人认为如果UI一直是按照一个规格给的,那么修改density是可以达到效果的。
    懒惰的勤奋:@拉丁吴 dp也需要转换成px吧。
    拉丁吴:@懒惰的勤奋 sw适配最终使用的是dp,
  • b3259bb55293:那文字怎么办?直接用还是用sp?
    拉丁吴:@吃货有爱 我是直接使用了dp,弄懂dp和sp的区别,自己做取舍咯
  • 阴天吃鱼:平时写布局的时候,我都是尽量使用match这样的,如果一行内有多个控件,就基本权重分割比例这样子,你写的这种的,听过但是没有用过。
    阴天吃鱼:@拉丁吴 嗯嗯,来学习一下,以后有机会拿来用用。哈哈
    拉丁吴:@她的梦z 这是个好习惯,但是具体的尺寸也是一定会用到的
  • 学习_猫:对了,在计算
    final float targetDensity = appDisplayMetrics.widthPixels / 360;
    结果都是整数,感觉对最终的适配有些影响,能改成appDisplayMetrics.widthPixels*1.0f / 360 吗
  • 航行在蓝天的蚂蚱:有导航栏的手机,分辨率值比原始值小
  • 天空的章末:试了下头条的,三星分屏的时候适配会出问题
    拉丁吴:怎么说?
  • 学习_猫:如果我的分辨率是1200*1920左右,但是dpi为320,sw能适配吗?
    拉丁吴:@小胖归来 如果你没有修改它的话,每个手机都是固定的
    学习_猫:使用这个修改完。
    DisplayMetrics metric = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metric);
    int width = metric.widthPixels; // 屏幕宽度(像素)
    int height = metric.heightPixels; // 屏幕高度(像素)
    float density = metric.density; // 屏幕密度

    这个获取的不会变化吗?
    拉丁吴:@小胖归来 能。
  • 有点健忘:平时一般都偷懒了。。不做适配。。
    JessYan:总结:

    在 1920*1080,dpi 为 480 的设备上,设计图上 50 * 50 的 view 最终高宽都为 144 px

    在 1920*1080,dpi 为 420 的设备上,设计图上 50 * 50 的 view 最终高宽都为 144.375 px

    可以看到虽然 dpi 不一样但最后的结果却是差不多的,所以即使在高宽一样,dpi 不一样的设备上 smallestWidth 方案也能完成等比例适配,所以这个方案是可行的

    根据我上面的计算,可以看出两个方案的最终原理其实差别不是太大,都是通过各种 dp,px,dpi 的换算来达到设计图的等比例缩放

    今日头条精确度是最完美的,但有一个问题所有地方都是一刀切,会影响老项目和三方库(可以选择某个 Activity 取消适配来缓解这个问题),但是使用起来非常简单,根本不需要什么成本,侵入性也低

    smallestWidth 的方案精确度稍微低一点,你需要使用了就自行引用 dimens,效果和范围都可控,不会像今日头头条一样一刀切,影响其他不需要的地方,使用上比今日头条的方案投入成本更高,侵入性略高,某些页面要实现以高为基准进行适配,或者项目中一些页面需要以高为基准,一些又要一宽为基准,实现上应该比较复杂

    所以这两个方案都是现阶段比较棒的方案,都可以达到等比例适配的效果,大家需要在投入成本和收益之间做出取舍,选择一个目前最适合自己项目的方案
    JessYan:下面同样来说说 smallestWidth 这个方案
    smallestWidth 方案的核心原理在于:
    当前设备总宽度 (单位为 dp)/ 设计图总宽度 (至于是 dp 还是 px 都不重要) = value (设计图上每个单位占当前设备多少 dp)
    然后根据这个 value 算出对应资源文件的 dimens,然后在项目中引用 dimens,系统会选择最合适的宽度的资源文件下的 dimens

    还是以上面的例子:
    假设设计图总宽度为 375,设备总宽度为 360 dp, 根据上面的公式 360 / 375 = 0.96 (意思是在当前设备上设计图的一个单位等于 0.96 dp,求出的结果也与上文中的截图一致)

    一个 View 在设计图上的尺寸是 50 * 50,使用这个方案我们直接在 layout 中引用 @dimen/qb_px_10,系统会选择 sw360dp 的资源文件,根据公式 50 * 0.96 = 48 dp,在当前设备上这个 View 的高宽都为 48 dp

    然后系统会根据公式 (dp * density = px) 把 dp 换算成 px

    测试设备总宽度为 1080 像素,dpi 为480,根据公式 dpi / 160 = density,480 / 160 = 3,求出 density 等于 3

    48 dp 会被系统换算成 144 px (48 * 3 = 144),这个这个 View 的最终高宽都是 144 px

    这个 View 的高宽,在设计图上占总宽度的比例是 (50 / 375 = 0.1333),在实际设备上占实际设备总宽度的比例是 (144 / 1080 = 0.1333),该 View 在设计图和实际设备上的比例是相同,所以完成了等比例缩放

    但这个方案由于没有自己强制更改 density,所以是系统根据 dpi 来求出 density,然后在根据公式 (dp * density = px) 来将 dp 换算成 px,所以这个方案的效果会受 dpi 影响的

    上文中提到过,同样的 1920*1080 的设备,虽然大部分的 dpi 为 480,但有些设备的 dpi 可能是不一样,比如 Pixel 2 的 dpi 为 420

    上面求出的 144 px 是在 1920*1080,dpi 为 480 的设备上

    现在我们试试高宽同样为 1920*1080,但是 dpi 为 420 的 Pixel 2 手机上是否也能完成等比例缩放

    根据公式 px / (dpi / 160) = dp, 1080 / (420 / 160) = 411.429 dp

    设备总宽度为 411.429 dp,使用 smallestWidth 方案生成 sw411dp 对应的资源文件

    根据公式 411 / 375 = 1.10,在当前 dpi 为 420 的设备上设计图的一个单位等于 1.10 dp

    一个在设计图上 50 * 50 的 View,在 dpi 为 420 的设备上,使用 sw411dp 得到的实际尺寸是 50 * 1.10 = 55 dp

    然后系统会根据公式 dp * density = px,把 dp 换算成 px

    55 * (420 / 160) = 144.375 px,这个 View 的最终高宽都是 144.375 px
    JessYan:感谢拉丁吴老师的贡献
    大概明白了这个方案,我看有很多人没理解,以下是我的理解,若有不对请指出

    简单拿今日头条的方案做对比,今日头条的方案, 核心原理在于:
    当前设备总宽度(单位为像素)/ 设计图总宽度(单位为 dp) = density
    density 的意思就是 1 dp 占当前设备多少像素
    假设设计图总宽度为 375 dp, 设备总宽度为 1080 px,根据上面的公式 1080 / 375 = 2.88 (density)
    一个 View 在设计图上的尺寸是 50dp * 50dp,所以在所有总宽度为 1080 px 的设备上,这个 View 的高宽都是 50 * 2.88 = 144 px (根据公式 dp * density = px)

    这个 View 的高宽,在设计图上占总宽度的比例是 (50 / 375 = 0.1333),在实际设备上占实际设备总宽度的比例是 (144 / 1080 = 0.1333),该 View 在设计图和实际设备上的比例是相同,所以完成了等比例缩放,该适配方案有效

    至于上文中提到的某些设备总宽度为 1080 px,但是 dpi 不同的设备,其实这个方案根本没有根据 dpi 求出 density,是根据自己的公式求出的 density, 所以这对今日头条的方案没有影响
  • 60c8e0743465:你好,请问高这个维度是怎么适配的
    成长的亚当:@JimmyZou 滑动延伸, 就是指用户可以滑动啊。
    388c93c4d16c:@拉丁吴 您好,很感激您的分享,请问高度这个滑动延伸是什么意思,能否简单告知一二,或者我应该上网查什么知识点?打搅了。
    拉丁吴:@BingoRiver 高这个维度是不需要适配的,因为布局可以滑动延伸
  • 君莫笑啊君莫笑_:哇,这个我要试试,谢谢大佬

本文标题:Android 目前最稳定和高效的UI适配方案

本文链接:https://www.haomeiwen.com/subject/vkmoeftx.html