精华内容
下载资源
问答
  • 这是一篇关于app设计师去了解下如何做android多分辨率多密度下...android界面适配的机制到底是咋样的呢?很通俗的点说就是:Android系统本身就提供了UI界面在多平台的适配的完善的解决方案和机制。并不是我们移动A...

    这是一篇关于app设计师去了解下如何做android多分辨率多密度下界面适配方案的原理解读。如果你已经知道了,可以不用再来阅读了。本文25学堂只是为了给一些app设计小白扫盲。让各位清楚的知道的android的UI界面到底是如何去解决适配问题的。

    94e6fe9ea04dbf39822ab7e8ed68fbe3.png

    android界面适配的机制到底是咋样的呢?

    很通俗的点说就是:Android系统本身就提供了UI界面在多平台的适配的完善的解决方案和机制。并不是我们移动APP设计师去解决适配的问题。或者是android程序员自己冥思苦想出来的最佳解决适配方案。

    因为进行android开发的时候,系统默认有这几个文件夹,

    2ee6f52d22c0041e1965e89873a05e51.png

    无论是android设计师或者android程序员把对应的ui设计资源放入对应的文档即可完成android UI 适配的最粗暴的解决方案!

    Android适配机制原理如下:

    Android适配机制就是在资源后面添加对这两种因素的限定,通过不同的限定区分不同的平台资源,Android在使用资源的时候会优先选择满足本平台限定的资源,再找最接近条件的,再找默认(即不加限定),通过选择适合当前平台的资源来完成不同平台的适配。

    屏幕尺寸分为:small,normal,large,xlarge分别表示小,中,大,超大屏

    屏幕密度分为:ldpi,mdpi,hdpi,xhdpi,它们的标准值分别是:120dpi,160dpi,240dpi,320dpi

    通俗点讲就是:系统根据当前的屏幕大小和密度,然后动态的采用程序中提供特定的资源。

    d03054cfbe305b9bb153a477fcedf20c.png

    如今最新的android适配资源需要五种:

    界面布局方面

    需要根据物理尺寸的大小准备5套布局

    layout: 放一些通用布局xml文件,比如界面中顶部和底部的布局,不会随着屏幕大小变化,类似windos窗口的title bar

    layout-small: 屏幕尺寸小于3英寸左右的布局

    layout-normal: 屏幕尺寸小于4.5英寸左右

    layout-large: 4英寸-7英寸之间

    layout-xlarge: 7-10英寸之间

    图片资源方面

    需要根据dpi值准备5套图片资源:

    drawable

    drawalbe-ldpi

    drawable-mdpi

    drawable-hdpi

    drawable-xhdpi

    Android有个自动匹配机制去选择对应的布局和图片资源。

    其实做法很简单,只需要在res目录下创建不同的layout文件夹,比如layout-640x360,layout-800x480,所有的layout文件在编译之后都会写入R.java里,而系统会根据屏幕的大小自己选择合适的layout进行使用。

    android UI适配主要受到屏幕分辨率和屏幕尺寸的影响,也是取决这2个因素。

    第二个大家需要注意的和疑问的:

    当我们使用切图工具Cutterman来进行安卓界面切图的时候,会自动生成10个文件夹。如图所示:

    570360947edc172f4b2020893af86935.png

    主要是因为最新版的安卓开发软件studio里面有了mipmap 目录和drawable 目录。两者都是一样的。

    唯一的区别:用mipmap系统会在缩放上提供一定的性能优化。

    比如:Nexus 6 有 493 ppi,它刚好在 xxhdpi和xxxhdpi之间,所以显示的时候需要对xxxhdpi的资源进行缩小,如果你用了mipmap-xxxhdpi,那么这里会对sclae有一个优化,性能更好,占用内存更少。所以现在官方推荐使用mipmap:

    最后再说下为什么在布局文件中使用 dp作为单位而不是使用px

    dp是一个与屏幕ppi无关的参数,同时也是一个事实上并不存在的虚拟单位。显示时会根据具体屏幕的ppi进行像素的映射,关系如下160ppi   1dp   1px120ppi   1dp   0.75px240ppi

    1dp   1.5px320ppi  1dp  2px因此在布局文件中一般使用dp作为单位,这样就能适应不同ppi的屏幕,在各种ppi的屏幕上有一样大的物理上的距离。

    (可以看出1dp并不总是等于1px的,对于ppi高的屏幕肯能要等于2px,只是为了物理上看起来的距离是相等的)

    展开全文
  • 1 Android屏幕适配的度量单位和相关概念 建议在阅读本文章之前,可以先阅读快乐李同学写的文章《Android屏幕适配的度量单位和相关概念》,这篇文章包含了阅读本文的一些基础知识,推荐阅读。 2 Android屏幕适配的...

    1 Android屏幕适配的度量单位和相关概念

    建议在阅读本文章之前,可以先阅读快乐李同学写的文章《Android屏幕适配的度量单位和相关概念》,这篇文章包含了阅读本文的一些基础知识,推荐阅读。

    2 Android屏幕适配的解决方案

    2.1 Android屏幕适配前言

    Android屏幕适配是一个亘古不变的难题,在百度或者Google搜索相关的关键词,我们总能找到各个年代所流行的Android屏幕适配方法。但是由于时效性的原因,2021年的今天出现了新的Android屏幕适配方法,并淘汰了部分以前旧的Android屏幕适配方法。于是,今天博主就来详细地总结一下,把迄今为止所有的Android屏幕适配方法都罗列出来,供大家参考和对比。

    早在2014年,支持的Android设备就有18796种了,而不像2021年的今天iOS设备的类型可能都没超过100种,下面这张图片所显示的内容足以充分说明当今Android系统碎片化问题的严重性,因为该图片中的每一个矩形都代表着一种Android设备。

    2

    就像Google Android开发者官网的《设备兼容性概览》文章所提到,这么多种类的Android设备都有着不同的屏幕尺寸和像素密度,尽管系统可通过基本的缩放和调整大小功能使界面适应不同屏幕,但您应做出进一步优化,以确保界面能够在各类屏幕上美观地呈现。

    以下的思维导图便是本文所要讲解内容的总览,也是结合Google Android开发者官网的相关Android屏幕适配文档后,总结的最全面的Android屏幕适配方案了。

    在这里插入图片描述

    2.2 适配不同屏幕尺寸的Android设备

    Android 设备的形状和尺寸多种多样,因此应用的布局需要十分灵活。也就是说,布局应该从容应对不同的屏幕尺寸和方向,而不是为布局定义刚性尺寸,假定屏幕尺寸和宽高比是一定的。

    通过支持尽可能多的屏幕,您的应用可以在各种不同设备上运行,这样您就可以使用单个 APK 将其提供给最多的用户。此外,如果能够使您的应用灵活适应不同的屏幕尺寸,可确保您的应用可以处理设备上的窗口配置更改,例如,当用户启用多窗口模式时发生的窗口配置更改。

    本页将向您介绍如何采用以下技巧支持不同的屏幕尺寸:

    • 使用允许布局调整大小的视图尺寸
    • 根据屏幕配置创建备用界面布局
    • 提供可以随视图一起拉伸的位图

    但是,请注意,适应不同的屏幕尺寸并不一定会使您的应用与所有 Android 设备类型兼容。您应该采取其他措施支持 Android Wear、Android TV、Android Auto 和 Chrome 操作系统设备

    有关针对不同屏幕构建界面的设计指南,请参阅自适应界面的 Material 指南

    2.2.1 灵活的布局

    无论您首先要支持什么硬件配置文件,都需要创建一个能够灵活应对屏幕尺寸变化(即便是微小变化)的布局。

    2.2.1.1 使用ConstraintLayout

    如需创建适用于不同屏幕尺寸的自适应布局,最佳做法是将 ConstraintLayout 用作界面中的基本布局。使用 ConstraintLayout,您可以根据布局中视图之间的空间关系指定每个视图的位置和大小。通过这种方式,当屏幕尺寸改变时,所有视图都可以一起移动和拉伸。

    如需使用 ConstraintLayout 构建布局,最简单的方法是使用 Android Studio 中的布局编辑器。借助该工具,您可以将新视图拖动到布局中,将其约束条件附加到父视图和其他同级视图以及修改视图的属性,完全不必手动修改任何 XML(请参见图 1)。

    如需了解详情,请参阅使用 ConstraintLayout 构建自适应界面
    在这里插入图片描述

    但是,ConstraintLayout 并不能解决所有布局场景(特别是动态加载的列表,对于此类布局,应使用 RecyclerView),但无论您使用何种布局,都应该避免对布局尺寸进行硬编码(请参见下一部分)。

    2.2.1.2 使用允许布局UI组件动态调整大小的视图属性,避免硬编码的布局尺寸(如100dp)

    2.2.1.2.1 优先用wrap_content和match_parent

    为了确保布局能够灵活地适应不同的屏幕尺寸,您应该对大多数视图组件的宽度和高度使用 "wrap_content""match_parent",而不是硬编码的尺寸,例如100dp。

    • "wrap_content" 指示视图将其尺寸设为适配该视图中相应内容所需的尺寸。

    • "match_parent" 使视图在父视图中尽可能地展开。

    例如用match_parent和wrap_content修饰的一个TextView,其代码如下:

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/lorem_ipsum" />
    

    虽然此视图的实际布局取决于其父视图和任何同级视图中的其他属性,但是此 TextView 想要将其宽度设为填充所有可用空间 (match_parent),并将其高度设为正好是文本长度所需的空间 (wrap_content)。这样可以使此视图适应不同的屏幕尺寸和不同的文本长度。

    下图显示了当屏幕宽度随着设备屏幕方向而发生变化时,如何使用 "match_parent" 调整文本视图的宽度。

    在这里插入图片描述

    2.2.1.2.2 使用搭配layout_weight的LinearLayout

    如果您使用的是 LinearLayout,则也可以按布局权重展开子视图,以便每个视图按自身权重值所占的比例填充剩余的空间。

    2.2.1.2.3 多层嵌套的LinearLayout可用ConstraintLayout或RelativeLayout优化

    但是,在嵌套的 LinearLayout 中使用权重将要求系统执行多次布局遍历以确定每个视图的尺寸,这会降低界面性能。幸运的是,ConstraintLayout 几乎能够构建 LinearLayout 所能构建的所有布局,而不会影响性能,因此您应该尝试将布局转换为 ConstraintLayout。然后,即可使用约束链来定义加权布局

    注意:使用 ConstraintLayout 时,不得使用 match_parent。而应将尺寸设为 0dp 以启用一种称为“匹配约束”的特殊行为,该行为通常与 match_parent 的预期行为相同。如需了解详情,请参阅如何调整 ConstraintLayout 中的视图尺寸

    2.2.2 备用布局

    虽然您的布局应始终通过拉伸其视图内部和周围的空间来应对不同的屏幕尺寸,但这可能无法针对每种屏幕尺寸提供最佳用户体验。例如,您为手机设计的界面或许无法在平板电脑上提供良好的体验。因此,您的应用还应提供备用布局资源,以针对特定屏幕尺寸优化界面设计。

    您可以通过创建额外的 res/layout/ 目录提供特定于屏幕的布局(针对需要不同布局的每种屏幕配置创建一个目录),然后将屏幕配置限定符附加到 layout 目录名称(例如,对于可用宽度为 600dp 的屏幕,附加限定符为 layout-w600dp)。

    这些配置限定符表示应用界面可用的可见屏幕空间。从应用中选择布局时,系统会考虑所有系统装饰(例如导航栏)和窗口配置更改(例如,当用户启用多窗口模式时)。

    如需在 Android Studio(使用 3.0 或更高版本)中创建备用布局,请按以下步骤操作:

    1. 打开默认布局,然后点击工具栏中的 Orientation for Preview 图标。
    2. 在下拉列表中,点击以创建一个建议的变体(如 Create Landscape Variant),或点击 Create Other
    3. 如果您选择 Create Other,系统将显示 Select Resource Directory。在此窗口中,在左侧选择一个屏幕限定符,然后将其添加到 Chosen qualifiers 列表中。添加限定符之后,点击 OK。(有关屏幕尺寸限定符的信息,请参阅下面几部分。)

    此时系统会在相应的布局目录中创建一个重复的布局文件,以便您可以开始自定义该屏幕变体的布局。

    2.2.2.1 最小宽度限定符(Smallest-Width)

    使用“最小宽度”屏幕尺寸限定符,您可以为具有最小宽度(以密度无关像素 dp 或 dip 为度量单位)的屏幕提供备用布局。

    通过将屏幕尺寸描述为密度无关像素的度量值,Android 允许您创建专为非常具体的屏幕尺寸而设计的布局,同时让您不必对不同的像素密度有任何担心。

    例如,您可以创建一个名为 main_activity 且针对手机和平板电脑进行了优化的布局,方法是在目录中创建该文件的不同版本,如下所示:

        res/layout/main_activity.xml           # For handsets (smaller than 600dp available width)
        res/layout-sw600dp/main_activity.xml   # For 7” tablets (600dp wide and bigger)
        
    

    最小宽度限定符指定屏幕两侧的最小尺寸,而不考虑设备当前的屏幕方向,因此这是一种指定布局可用的整体屏幕尺寸的简单方法。

    下面是其他最小宽度值与典型屏幕尺寸的对应关系:

    • 320dp:典型手机屏幕(240x320 ldpi、320x480 mdpi、480x800 hdpi 等)。
    • 480dp:约为 5 英寸的大手机屏幕 (480x800 mdpi)。
    • 600dp:7 英寸平板电脑 (600x1024 mdpi)。
    • 720dp:10 英寸平板电脑(720x1280 mdpi、800x1280 mdpi 等)。

    下图提供了一个更详细的视图,说明了不同屏幕 dp 宽度与不同屏幕尺寸和方向的一般对应关系。
    在这里插入图片描述

    请记住,最小宽度限定符的所有数值都是密度无关像素,因为重要的是系统考虑像素密度(而不是原始像素分辨率)之后可用的屏幕空间量。

    注意:您使用这些限定符指定的尺寸不是实际屏幕尺寸,而是 Activity 窗口可用的宽度或高度(以 dp 为单位)。Android 系统可能会将部分屏幕用于系统界面(如屏幕底部的系统栏或顶部的状态栏),因此部分屏幕可能不可供您的布局使用。如果您的应用在多窗口模式下使用,则它只能使用该窗口的尺寸。对该窗口进行大小调整时,它会使用新窗口尺寸触发配置更改,以便系统可以选择适当的布局文件。因此,在声明尺寸时,您应具体说明 Activity 需要的尺寸。在声明为布局提供的空间时,系统会考虑系统界面使用的所有空间。

    2.2.2.2 可用宽度限定符

    您可能希望根据当前可用的宽度或高度来更改布局,而不是根据屏幕的最小宽度来更改布局。例如,如果您有一个双窗格布局,您可能希望在屏幕宽度至少为 600dp 时使用该布局,但屏幕宽度可能会根据设备的屏幕方向是横向还是纵向而发生变化。在这种情况下,您应使用“可用宽度”限定符,如下所示:

    res/layout/main_activity.xml         # For handsets (smaller than 600dp available width)
    res/layout-w600dp/main_activity.xml  # For 7” tablets or any screen with 600dp available width (possibly landscape handsets)
    

    如果您关心可用高度,则可以使用“可用高度”限定符来执行相同的操作。例如,对于屏幕高度至少为 600dp 的屏幕,请使用限定符 layout-h600dp

    2.2.2.3 屏幕方向限定符

    虽然您可能只需将“最小宽度”和“可用宽度”限定符结合使用即可支持所有尺寸变化,但是您可能还希望当用户在纵向与横向之间切换屏幕方向时改变用户体验。

    为此,您可以将 portland 限定符添加到资源目录名称中。只需确保这些限定符在其他尺寸限定符后面即可。例如:

    res/layout/main_activity.xml                # For handsets
    res/layout-land/main_activity.xml           # For handsets in landscape
    res/layout-sw600dp/main_activity.xml        # For 7” tablets
    res/layout-sw600dp-land/main_activity.xml   # For 7” tablets in landscape
    

    如需详细了解所有屏幕配置限定符,请参阅提供资源指南中的表 2。

    2.2.2.4 Fragment将界面UI组件模块化

    在针对多种屏幕尺寸设计应用时,您希望确保不会在 Activity 之间不必要地重复界面行为。因此,您应该使用 Fragment 将界面逻辑提取到单独的组件中。然后,您可以组合 Fragment 以便在大屏幕设备上运行时创建多窗格布局,或者在手机上运行时将 Fragment 放置在单独的 Activity 中。

    例如,平板电脑上的一款新闻应用可能在左侧显示报道列表,而在右侧显示一篇完整的报道。在左侧选择一篇报道时,会更新右侧的报道视图。但是,在手机上,这两个组件应显示在单独的屏幕上。从列表中选择一篇报道时,会改变整个屏幕以显示这篇报道。

    如需了解详情,请参阅使用 Fragment 构建动态界面

    2.2.2.5 使用旧尺寸限定符支持Android3.1及之前的设备

    如果您的应用支持 Android 3.1(API 级别 12)或更低版本,则除上面的最小/可用宽度限定符之外,您还需要使用旧尺寸限定符

    在上面的示例中,如果要在较大的设备上显示双窗格布局,那么您需要使用“large”配置限定符来支持 3.1 及更低版本。因此,要在这些旧版本上实现此类布局,您可能需要创建以下文件:

    res/layout/main_activity.xml           # For handsets (smaller than 640dp x 480dp)
    res/layout-large/main_activity.xml     # For small tablets (640dp x 480dp and bigger)
    res/layout-xlarge/main_activity.xml    # For large tablets (960dp x 720dp and bigger)
    

    如果同时支持低于和高于 Android 3.2 的版本,您必须同时对布局使用最小宽度限定符和“large”限定符。因此,您应创建一个名为 res/layout-large/main.xml 的文件,该文件可能与 res/layout-sw600dp/main.xml 完全相同。

    为避免同一文件出现这种重复,您可以使用别名文件。例如,您可以定义以下布局:

    res/layout/main.xml            # single-pane layout
    res/layout/main_twopanes.xml   # two-pane layout
    

    并添加以下两个文件:

    • res/values-large/layout.xml:

      <resources>
          <item name="main" type="layout">@layout/main_twopanes</item>
      </resources>
      
    • res/values-sw600dp/layout.xml:

      <resources>
          <item name="main" type="layout">@layout/main_twopanes</item>
      </resources>
      

    这两个文件的内容完全相同,但它们实际上并未定义布局,而只是将 main 设置为 main_twopanes 的别名。由于这些文件具有 largesw600dp 选择器,因此它们适用于任何 Android 版本的平板电脑和电视(低于 3.2 版本的平板电脑和电视与 large 匹配,高于 3.2 版本者将与 sw600dp 匹配)。

    2.2.3 可拉伸的九宫格位图

    如果您在改变尺寸的视图中将位图用作背景,您会注意到,当视图根据屏幕尺寸或视图中的内容增大或缩小时,Android 会缩放您的图片。这通常会导致明显的模糊或其他缩放失真。解决方案是使用九宫格位图,这种特殊格式的 PNG 文件会指示哪些区域可以拉伸,哪些区域不可以拉伸。

    九宫格位图基本上是一种标准的 PNG 文件,但带有额外的 1 像素边框,指示应拉伸哪些像素(并且带有 .9.png 扩展名,而不只是 .png)。如图 5 中所示,左边缘和上边缘的黑线之间的交点是可以拉伸的位图区域。

    或者,您也可以定义内容在视图内应进入的安全区域,方法是以同样的方式在右边缘和下边缘添加线条。

    将九宫格作为背景应用于视图时,框架会正确拉伸图片以适应按钮的尺寸。

    如需根据位图创建九宫格图片方面的帮助,请参阅创建可调整大小的位图

    2.3 适配不同像素密度ppi的Android设备

    Android 设备不仅有不同的屏幕尺寸(手机、平板电脑、电视等),而且其屏幕也有不同的像素尺寸。也就是说,有可能一部设备的屏幕为每平方英寸 160 像素,而另一部设备的屏幕为每平方英寸 480 像素。如果您不考虑像素密度的这些差异,系统可能会缩放图片(导致图片变模糊),或者图片可能会以完全错误的尺寸显示。

    本页向您介绍如何设计应用来支持不同的像素密度,那就是使用分辨率无关度量单位,并针对每种像素密度提供备用位图资源。

    2.3.1 使用密度无关像素dp/dip

    密度无关像素是一个基于屏幕物理密度的度量单位,在160dpi的屏幕中1dp大约等于1px。当在更高密度的屏幕上运行时,用于绘制1dp的像素数量会被一个适合屏幕dpi的density因子放大,例如在320dpi的屏幕中1dp大约等于2px。而在低密度屏幕上,1dp的像素数量会减少。

    也就是说,dp与px的比值与屏幕物理密度成正相关,但不一定成正比。度量单位dp可以在布局中适当地调整UI组件的大小,以适合不同的屏幕密度。换句话说,它为您在不同设备上的UI元素的真实大小提供了一致性。

    为什么说dp与px的比值与屏幕物理密度成正相关,而不是成正比呢?


    这主要是因为Android开发者可以在代码中指定浮点数的dp或者px,但当在设备屏幕上按照对应的dp或px显示内容时要进行四舍五入取整,因为设备屏幕都是一个个像素点构成的,因此它们的关系是成正相关。


    举个例子,一个宽高为11dp*11dp的ImageView,在density为1.0的设备屏幕上显示的实际像素为11px*11px,但在density为1.5的设备屏幕上显示的实际像素为17px*17px,而不是16.5px*16.5px。因为一个1px*1px的像素点只能是要么显示,要么不显示,所以Android系统中会将16.5px*16.5px四舍五入取整为17px*17px。

    Android官方文档《支持不同的像素密度》对于屏幕适配的观点是:必须避免的第一个陷阱是使用像素px来定义距离或尺寸。使用像素来定义尺寸会带来问题,因为不同的屏幕具有不同的像素密度,所以同样数量的像素在不同的设备上可能对应于不同的物理尺寸。

    例如下方同样是4.0英寸的两部手机,左边那台手机的分辨率很低,是320px*180px,右边那台是960px*540px。如果将显示字母a图片的ImageView宽高都设置为100px,那么左边手机显示的字母a图片很大,而右边手机显示的字母a图片很小。

    只有将显示字母a图片的ImageView宽高都设置为100dp,才能出现下面的效果,即该字母a图片在两台分辨率不同的手机看起来实际的物理宽高差不多一致,而不是一大一小。

    要在密度不同的屏幕上保持一个UI组件显示出相同的实际物理宽高,您必须使用密度无关像素 (dp) 作为度量单位来设计界面。dp是一个虚拟像素单位,1dp约等于在在基准密度160dpi屏幕上的1px。对于其他每个密度,Android 会将此值转换为相应的实际像素数。

    2.3.2 提供备用位图

    要在像素密度不同的设备上提供良好的图形质量,您应该以相应的分辨率在应用中提供每个位图的多个版本(针对每个密度级别提供一个版本)。否则,Android 系统必须缩放位图,使其在每个屏幕上占据相同的可见空间,从而导致缩放失真,如模糊。

    在这里插入图片描述

    您的应用中有多个密度级别可供使用。下表描述了可用的不同配置限定符以及它们适用的屏幕类型。

    表 1. 适用于不同像素密度的配置限定符。

    密度限定符说明
    ldpi适用于低密度 (ldpi) 屏幕 (~ 120dpi) 的资源。
    mdpi适用于中密度 (mdpi) 屏幕 (~ 160dpi) 的资源(这是基准密度)。
    hdpi适用于高密度 (hdpi) 屏幕 (~ 240dpi) 的资源。
    xhdpi适用于加高 (xhdpi) 密度屏幕 (~ 320dpi) 的资源。
    xxhdpi适用于超超高密度 (xxhdpi) 屏幕 (~ 480dpi) 的资源。
    xxxhdpi适用于超超超高密度 (xxxhdpi) 屏幕 (~ 640dpi) 的资源。
    nodpi适用于所有密度的资源。这些是与密度无关的资源。无论当前屏幕的密度是多少,系统都不会缩放以此限定符标记的资源。
    tvdpi适用于密度介于 mdpi 和 hdpi 之间的屏幕(约 213dpi)的资源。这不属于“主要”密度组。它主要用于电视,而大多数应用都不需要它。对于大多数应用而言,提供 mdpi 和 hdpi 资源便已足够,系统将视情况对其进行缩放。如果您发现有必要提供 tvdpi 资源,应按一个系数来确定其大小,即 1.33*mdpi。例如,如果某张图片在 mdpi 屏幕上的大小为 100px x 100px,那么它在 tvdpi 屏幕上的大小应该为 133px x 133px。

    要针对不同的密度创建备用可绘制位图资源,您应遵循六种主要密度之间的 3:4:6:8:12:16 缩放比。例如,如果您有一个可绘制位图资源,它在中密度屏幕上的大小为 48x48 像素,那么它在其他各种密度的屏幕上的大小应该为:

    • 36x36 (0.75x) - 低密度 (ldpi)
    • 48x48(1.0x 基准)- 中密度 (mdpi)
    • 72x72 (1.5x) - 高密度 (hdpi)
    • 96x96 (2.0x) - 超高密度 (xhdpi)
    • 144x144 (3.0x) - 超超高密度 (xxhdpi)
    • 192x192 (4.0x) - 超超超高密度 (xxxhdpi)

    然后,将生成的图片文件置于 res/ 下的相应子目录中,系统将自动根据运行您的应用的设备的屏幕密度选取正确的文件:

    res/
    	drawable/
    		awesome-image.png
    	drawable-nodpi/
    		awesome-image.png
    	drawable-xxxhdpi/
    		awesome-image.png
    	drawable-xxhdpi/
    		awesome-image.png
    	drawable-xhdpi/
    		awesome-image.png
    	drawable-hdpi/
    		awesome-image.png
    	drawable-mdpi/
    		awesome-image.png
    

    之后,每当您引用 @drawable/awesome-image 时,系统便会根据屏幕 dpi 选择相应的位图。如果您没有为某个密度提供特定于密度的资源,那么系统会选取下一个最佳匹配项并对其进行缩放以适合屏幕。

    举个例子,drawable、drawable-mdpi、drawable-xhdpi文件夹下都有对应分辨率图片资源image1.png:

    1. 那么xhdpi的设备直接在drawable-xhdpi文件夹下找到该图片,接着显示到屏幕上;

    2. 而hdpi的设备先在drawable-hdpi文件夹下没找到该图片,再去drawable-xhdpi文件夹下找到该图片,并将其缩小到hdpi对应的分辨率,接着显示到屏幕上;

    3. 而xxhdpi的设备先在drawable-xxhdpi文件夹下没找到该图片,再去drawable-xxxhdpi文件夹下也没找到该图片,最后只能退而求其次去drawable-xhdpi文件夹下找到该图片,并将其放大到xxhdpi对应的分辨率(注意这个放大过程会失真),接着显示到屏幕上。

    再举个例子,只有drawable文件夹下有图片资源image2.png,那么那么xhdpi的设备会按照drawable-xhdpi、drawable-xxhdpi、drawable-xxxhdpi、drawable-hdpi、drawable-mdpi、drawable文件夹从前到后的优先级顺序去查找这个图片资源,最终是在drawable文件夹中找到该图片资源,并将其放大或缩小到xhdpi对应的分辨率(放大会失真,缩小则不会),接着显示到屏幕上。

    提示:如果您有一些系统应该永远不会缩放(或许是因为您在运行时亲自对图像做一些调整)的可绘制对象资源,则应将它们放在有 nodpi 配置限定符的目录中。使用此限定符的资源被视为与密度无关,系统不会缩放它们。

    如需详细了解其他配置限定符以及 Android 如何根据当前屏幕配置选择适当的资源,请参阅提供资源

    2.3.2.1 位图错放密度限定符文件夹的后果

    如果有一张1MB大小、3500px*2500px的image.jpg图片,那么它在APP中加载会占用多少内存呢?是不是也是占用1MB的内存?

    首先我们要知道默认情况下,一个像素占用4个字节,因为Android默认采用ARGB来表示颜色,例如#FFFF7A25表示完全不透明的橙色。接着我们来看看计算APP中加载图片所占用内存的公式:

    APP中加载图片所占用内存=横向像素数*纵向像素数*每个像素占用的内存(默认为4)

    因此一张1MB大小、3500px*2500px的image.jpg图片,它在APP中所占用内存为3500*2500*4/1024/1024≈33.3786MB,这么一张大分辨率的图片足以让APP抛出一个OOM内存溢出的异常。

    那么为什么一张实际占用空间大小为1MB的图片在APP中加载图片所占用内存为33.3786MB呢,这主要是因为该图片是应用了相应压缩算法的jpg图片,而在Android中使用Bitmap时需要该图片的原始数据,所以要按照上述的公式进行计算。

    如果把该图片仅存放于drawable-mdpi文件夹下,而屏幕密度对应为xhdpi的设备在APP中加载该图片会占用约133.5144MB的内存,是原来理论值33.3786MB的四倍,这就更容易导致内存溢出了。这主要是因为Android系统会把drawable-mdpi文件夹下的该图片的宽和高各自扩大2倍,以保证该图片能在屏幕密度对应为xhdpi的设备上正常显示。

    再举另一个例子,如果把该图片仅存放且正确地存放于drawable-hxdpi文件夹下,而屏幕密度对应为mdpi的设备在APP中加载该图片会占用约8.3447MB的内存,是原来理论值33.3786MB的四分之一。但是Google仍然不推荐我们只存放xhdpi分辨率下的该图片,而是应该提供mdpi和hdpi分辨率下备用位图,这样屏幕密度对应为mdpi的设备就能直接加载drawable-mdpi文件夹下的该图片,就不用再从drawable-xhdpi文件夹中取出该图片再进行缩小了,因为这个缩小过程也是会消耗系统资源的,这也是为什么Google要求我们为mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi屏幕密度设备提供备用位图的原因。

    具体的比例关系如下图所示(NX表示表示宽和高是mdpi的N倍,总内存就是N*N倍):
    在这里插入图片描述
    这也是为什么Android要求备用位图要放在对应密度限定符的文件夹中,而位图错放密度限定符的文件夹可能导致OOM内存溢出、消耗不必要的系统资源等问题。

    那么在将仅存在于drawable-xhdpi文件夹下的某图片缩小到适配mdpi屏幕分辨率设备的过程中,一定要将此图片完整地加载进入进内存再进行缩小的过程吗?

    当然不是,可以使用BitmapFactory.decode()传入一个Options对象,进行某些设置即可读取到该图片的宽高和类型,而不需要读取整个图片到内存中。读取了图片的宽高属性后,可以按照想要的比例进行缩放读取。我的理解是如果你设置的压缩比是2(横向纵向像素各缩小2倍,总压缩比4倍),在BitmapFactory进行decode的时候知道了你的压缩比,它会选择性的读取某些像素点(具体算法未知,可以简单的想成比如原图是100px*100px,现在我们获得到了它的输入流,只是平均地从输入流中读取50px*50px的像素)。这样就实现了大图片不加载到内存先压缩的过程。

    这种做法可以用在本地图片的显示处理,同时也可以用在加载网络图片的时候。都是为了避免OOM,同时实现对大图片进行压缩。

    2.3.3 mipmap目录存放应用图标

    与其他所有位图资源一样,对于应用图标,您也需要提供特定于密度的版本。不过,某些应用启动器显示的应用图标会比设备的密度级别所要求的大差不多 25%。

    例如,如果设备的密度存储分区为 xxhdpi,而您提供的最大应用图标密度级别为 drawable-xxhdpi,则启动器应用会放大此图标,这会导致它看起来不太清晰。因此,您应在 mipmap-xxxhdpi 目录中提供一个密度更高的启动器图标,而后启动器便可改用 xxxhdpi 资源。

    由于应用图标可能会像这样放大,因此您应将所有应用图标都放在 mipmap 目录中,而不是放在 drawable 目录中。与 drawable 目录不同,所有 mipmap 目录都会保留在 APK 中,即使您构建特定于密度的 APK 也是如此。这样,启动器应用便可选取要显示在主屏幕上的最佳分辨率图标。

    res/
    	mipmap-xxxhdpi/
    		launcher-icon.png
    	mipmap-xxhdpi/
    		launcher-icon.png
    	mipmap-xhdpi/
    		launcher-icon.png
    	mipmap-hdpi/
    		launcher-icon.png
    	mipmap-mdpi/
    		launcher-icon.png
    

    有关图标设计指南,请参阅图标的素材指南。如需构建应用图标方面的帮助,请参阅使用 Image Asset Studio 创建应用图标

    2.4 适配以dp为单位的不同最小宽度的Android设备

    2.4.1 以px为度量单位的基于屏幕分辨率限定符的百分比布局(2015年已废弃方案)

    苦于Android屏幕碎片化的问题,最早在2015年5月,张鸿洋在其文章《Android 屏幕适配方案》中提出了以px为度量单位的基于屏幕分辨率限定符的百分比布局的Android屏幕适配方案,后文中该方案会简称为“屏幕分辨率限定符Android屏幕适配方案”。其原理主要是在 res 文件夹下创建各种屏幕分辨率对应的 values-xxx 文件夹,然后根据一个基准分辨率,生成各种分辨率对应的 dimens.xml 文件。

    例如基准分辨率为 1280x720,将宽度分成 720 份,取值为 1px~720px,将高度分成 1280 份,取值为 1px~1280px。假设设计图上的一个控件的宽度为 720px,那么布局中就写 android:layout_width="@dimen/x720" ,当运行程序的时候,系统会根据设备的分辨率去寻找对应的 dimens.xml 文件。例如运行在分辨率为 1280x720 的设备上,系统会自动找到对应的 values-1280x720 文件夹下的 lay_x.xml 文件,由上图可知 x720 对应的值为720.px,可铺满该屏幕宽度。运行在分辨率为 1920x1080 的设备上,系统会自动找到对应的 values-1920x1080 文件夹下的 lay_x.xml 文件,由上图可知 x720 对应的值为 1080.0px,可铺满该屏幕宽度。这样就达到了屏幕适配的要求。

    但是这种Android屏幕适配方案存在着很大的缺陷:

    1. 屏幕分辨率限定符适配是以屏幕分辨率为基准的,Android 设备分辨率一大堆,而且还要考虑部分带有虚拟导航栏的Android设备,因为带和不带虚拟导航栏的Android设备的APP可用屏幕分辨率是不同的,这样就需要大量的 dimens.xml 文件;
    2. 屏幕分辨率限定符适配采用的是 px 单位,而Google在Android开发者官方文档《支持不同的像素密度》中对于屏幕适配的观点是:必须避免的第一个陷阱是使用像素px来定义距离或尺寸;
    3. 屏幕分辨率限定符适配需要设备分辨率与 values-xx 文件夹完全匹配才能达到适配,如果数值有一点点差距都无法匹配,因此这也导致需要大量的dimens.xml 文件。

    也正是由于有以上的缺陷,后续在2018年4月简书博主Wildma提出了以dp为度量单位的基于最小宽度限定符的百分比布局的Android屏幕适配方案。

    2.4.2 以dp为度量单位的基于最小宽度限定符的百分比布局(2018年方案)

    基于屏幕分辨率限定符Android屏幕适配方案的灵感,在2018年4月简书博主Wildma发表了文章《一种非常好用的Android屏幕适配—smallestWidth 限定符适配》,提出了以dp为度量单位的基于最小宽度限定符的百分比布局的Android屏幕适配方案,后文中该方案会简称为“最小宽度限定符Android屏幕适配方案”。

    该适配方案的原理便是以设计图最小宽度(单位为 dp)作为基准值,利用插件生成所有设备对应的 dimens.xml 文件,再根据设计图标注,标注多少 dp,布局中就写多少dp,格式为@dimen/dp_XX,最后系统都是根据限定符去寻找对应的 dimens.xml 文件。

    例如程序运行在最小宽度为 360dp 的设备上,系统会自动找到对应的 values-sw360dp 文件夹下的 dimens.xml 文件。区别就在于屏幕分辨率限定符适配是拿 px 值等比例缩放,而最小宽度限定符Android屏幕适配方案是拿 dp 值来等比缩放而已。需要注意的是“最小宽度”是不区分方向的,即无论是宽度还是高度,哪一边小就认为哪一边是“最小宽度”。

    相比于屏幕分辨率限定符Android屏幕适配方案,最小宽度限定符Android屏幕适配方案有以下优势:

    1. 屏幕分辨率限定符适配是以屏幕分辨率为基准的,Android 设备分辨率一大堆,而且还要考虑部分带有虚拟导航栏的Android设备,因为带和不带虚拟导航栏的Android设备的APP可用屏幕分辨率是不同的,这样就需要大量的 dimens.xml 文件。但是无论手机屏幕的像素是多少、密度是多少,90%的手机的最小宽度都为360dp,所以采用最小宽度限定符Android屏幕适配方案只需要少量 dimens.xml 文件即可;
    2. 屏幕分辨率限定符适配采用的是 px 单位,而最小宽度限定符Android屏幕适配方案采用的单位是 dp 和 sp,dp 和 sp 是 google 推荐使用的计量单位。又由于很多应用要求字体大小随系统改变,所以字体单位使用 sp 也更灵活;
    3. 屏幕分辨率限定符适配需要设备分辨率与 values-xx 文件夹完全匹配才能达到适配,而最小宽度限定符Android屏幕适配方案寻找 dimens.xml 文件的原理是从大往小找,例如设备的最小宽度为 360dp,就会先去找 values-360dp,发现没有则会向下找 values-320dp,如果还是没有才找默认的 values 下的 demens.xml 文件,所以即使没有完全匹配也能达到不错的适配效果。

    但是最小宽度限定符Android屏幕适配方案也不是万能的,它也有一个严重的缺陷:

    布局UI组件宽高属性的描述符不具有通用性。绝大部分的Android项目中,布局UI组件宽高属性的描述符都是"XXdp",而该方案用的是"@dimen/dp_XX",那么之前使用"XXdp"描述符的Android老项目需要一一进行修改,既要考虑"XXdp"描述符和"@dimen/dp_XX"描述符转换的问题。另外如果引入一些第三方的自定义View,那么这些第三方的自定义View的宽高属性可能无法被此方案进行统一控制。

    但总的来说,最小宽度限定符Android屏幕适配方案也非常优秀,如果想要体验这个项目,可访问该适配方案的GitHub项目链接地址:ScreenAdaptation

    2.4.3 使用ConstraintLayout的比例约束功能(2018年至今最优方案)

    早在2016年,Google便推出了支持Android2.3及以上版本的约束布局(ConstraintLayout) V1.0,它的出现主要是为了解决布局嵌套过多的问题,以灵活的方式定位和调整小部件。也是从 Android Studio 2.3 起,新建的Activity默认所带的模板xml文件便是使用ConstraintLayout

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".activity.MainActivity">
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    但真正引起Android开发者激动的是在2018年发布的V1.1稳定版的ConstraintLayout,新版本引入了按照比例约束控件位置和尺寸的新功能,能够更好地适配屏幕大小不同的机型。而这个功能便是相比于最小宽度限定符Android屏幕适配方案更优的选择。布局UI控件指定"app:layout_constraintWidth_percent"和"app:layout_constraintHeight_percent"属性,来约束布局UI控件的宽高在可用空间中的百分比比例。因此,使用几行 XML 代码就可以使 Button 或 TextView 展开并以百分比填充屏幕:

    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintWidth_percent="0.7"
        android:text="70% WIDTH" />
    

    在这里插入图片描述

    有些人考虑到了嵌套布局带来的风险,所以用一个RelativeLayout来装下所有的控件。那么问题来了,既然用RelativeLayout可以解决问题,为什么还要使用ConstraintLayout呢?因为ConstraintLayout使用起来比RelativeLayout更灵活,性能更出色!还有一点就是ConstraintLayout可以按照比例约束控件位置和尺寸,能够更好地适配屏幕大小不同的机型。

    相比于多层嵌套的LinearLayout和RelativeLayout,为什么优选ConstraintLayout?


    在开发过程中经常能遇到一些复杂的UI,可能会出现布局嵌套过多的问题,嵌套得越多,设备绘制视图所需的时间和计算功耗也就越多。

    有些人考虑到了嵌套布局带来的风险,所以用一个RelativeLayout来装下所有的控件。那么问题来了,既然用RelativeLayout可以解决问题,为什么还要使用ConstraintLayout呢?因为ConstraintLayout使用起来比RelativeLayout更灵活,性能更出色!还有一点就是ConstraintLayout可以按照比例约束控件位置和尺寸,能够更好地适配屏幕大小不同的机型。

    2.5 适配刘海屏(Display Cutouts)设备

    刘海屏是指某些设备显示屏上的一个区域延伸到显示面,这样既能为用户提供全面屏体验,又能为设备正面的重要传感器留出空间。Android 在搭载 Android 9(API 级别 28)及更高版本的设备上正式支持刘海屏。请注意,设备制造商也可以选择在搭载 Android 8.1 或更低版本的设备上支持刘海屏。

    本主题介绍如何实现对带刘海屏的设备的支持,包括如何处理“刘海区域”,即显示面上包含刘海的无边框矩形。

    2.5.1 刘海屏设备所具备的特质

    为了确保一致性和应用兼容性,搭载 Android 9 的设备必须确保以下刘海行为:

    • 一条边缘最多只能包含一个刘海;
    • 一台设备不能有两个以上的刘海;
    • 设备的两条较长边缘上不能有刘海;
    • 在未设置特殊标志的竖屏模式下,状态栏的高度必须至少与刘海的高度持平;
    • 默认情况下,在全屏模式或横屏模式下,整个刘海区域必须显示黑边。

    2.5.2 APP处理刘海区域的方案

    如果不希望您的内容与刘海区域重叠,请确保您的内容不与状态栏和导航栏重叠,这样做一般就足够了。如果您要将内容呈现到刘海区域中,则可以使用 WindowInsets.getDisplayCutout() 来检索 DisplayCutout 对象,该对象包含每个刘海区域的安全边衬区和边界框。您可以使用这些 API 来检查您的内容是否与刘海区域重叠,以便根据需要重新放置。

    注意:要在多个 API 级别管理刘海实现,您还可以使用 AndroidX 库(可通过 SDK 管理器获得)中的 DisplayCutoutCompat

    Android 还允许您控制是否在刘海区域内显示内容。窗口布局属性 layoutInDisplayCutoutMode 控制您的内容如何呈现在刘海区域中。您可以将 layoutInDisplayCutoutMode 设为以下某个值:

    您可以通过编程或在 Activity 中设置样式来设置刘海模式。以下示例定义了一种样式,您可以使用它将LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES属性应用到 Activity。

    <style name="ActivityTheme">
      <item name="android:windowLayoutInDisplayCutoutMode">
        shortEdges <!-- default, shortEdges, never -->
      </item>
    </style>
    

    下面几部分更详细地介绍了不同的刘海模式。

    2.5.2.1 默认方案

    默认情况下,在未设置特殊标志的竖屏模式下,在带刘海屏的设备上,状态栏的大小会调整为至少与刘海一样高,而您的内容会显示在下方区域。在横屏模式或全屏模式下,您的应用窗口会显示黑边,因此您的任何内容都不会显示在刘海区域中。

    2.5.2.2 将内容呈现在短边刘海区域中

    对于某些内容(如视频、照片、地图和游戏),呈现在刘海区域中是一种很好的方法,这样能够为用户提供沉浸感更强的全面屏体验。如果设置了 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES,则在竖屏模式和横屏模式下,内容都会延伸到显示屏的短边上的刘海区域,而不管系统栏处于隐藏还是可见状态。请注意,窗口无法延伸到屏幕的长边上的刘海区域。使用此模式时,请确保没有重要内容与刘海区域重叠。

    请注意,Android 可能不允许内容视图与系统栏重叠。要替换此行为并强制内容延伸到刘海区域,请通过 View.setSystemUiVisibility(int) 方法将以下任一标志应用于视图可见性:

    • SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    • SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    • SYSTEM_UI_FLAG_LAYOUT_STABLE

    下面是一些 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 示例:

    请注意,边角处的刘海可等同于在短边上,因此适用同样的行为:

    2.5.2.3 从不将内容呈现在刘海区域中

    如果设置了 LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER,则不允许窗口与刘海区域重叠。

    此模式应该用于暂时设置 View.SYSTEM_UI_FLAG_FULLSCREENView.SYSTEM_UI_FLAG_HIDE_NAVIGATION 的窗口,以避免在设置或清除了该标志时执行另一种窗口布局。

    请查看下面的 LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 示例:

    在这里插入图片描述

    2.5.3 适配刘海屏的最佳方案

    使用刘海屏时,请务必考虑以下几点:

    • 不要让刘海区域遮盖任何重要的文本、控件或其他信息。

    • 不要将任何需要精细轻触识别的交互式元素放置或延伸到刘海区域。刘海区域中的轻触灵敏度可能要比其他区域低一些。

    • 避免对状态栏高度进行硬编码,因为这样做可能会导致内容重叠或被切断。如有可能,请使用 WindowInsetsCompat 检索状态栏高度,并确定要对您的内容应用的适当内边距。

    • 不要假定应用会占据整个窗口,而应使用 View.getLocationInWindow() 来确认应用的位置。不要使用 View.getLocationOnScreen()

    • 务必妥善处理进入或退出全屏模式。请阅读这篇 Android 开发者博文

    • 对于竖屏模式下的默认刘海行为,如果刘海区域位于顶部边缘,并且窗口未设置 FLAG_FULLSCREENView.SYSTEM_UI_FLAG_FULLSCREEN,则窗口可以延伸到刘海区域。同样,如果刘海区域位于底部边缘,并且窗口未设置 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION,则窗口可以延伸到刘海区域。在全屏模式或横屏模式下,窗口的布局方式应确保其不与刘海区域重叠。

    • 如果您的应用需要进入和退出全屏模式,请使用 shortEdgesnever 刘海模式。默认刘海行为可导致应用中的内容在全屏模式转换过程中上下移动,如下图所示:

    • 在全屏模式下,在使用窗口坐标与屏幕坐标时应保持谨慎,因为在显示黑边的情况下,您的应用不会占据整个屏幕。由于显示黑边,因此根据屏幕原点得到的坐标与根据窗口原点得到的坐标不再相同。您可以根据需要使用 getLocationOnScreen() 将屏幕坐标转换为视图坐标。下图展示了内容显示黑边时这两种坐标有何不同:
      在这里插入图片描述
      处理 MotionEvent 时,请使用 MotionEvent.getX()MotionEvent.getY() 来避免类似的坐标问题。不要使用 MotionEvent.getRawX()MotionEvent.getRawY()

    2.5.4 测试APP在刘海屏设备的呈现效果

    在这里插入图片描述

    请务必测试应用的所有屏幕和体验。如有可能,在具有不同类型刘海屏的设备上进行测试。如果您没有带刘海屏的设备,可以在搭载 Android 9 的任意设备或模拟器上模拟一些常见的刘海配置,具体操作步骤如下:

    1. 启用开发者选项
    2. 开发者选项屏幕中,向下滚动到绘制部分,然后选择模拟刘海屏。
    3. 选择刘海类型。

    本文参考文献:

    Android开发者-文档-指南-应用资源概览

    Android开发者-文档-指南-设备兼容性-设备兼容性概览

    Android开发者-文档-指南-设备兼容性-支持不同的屏幕尺寸

    Android开发者-文档-指南-设备兼容性-支持不同的像素密度

    Android开发者-文档-指南-设备兼容性-支持刘海屏

    【Android屏幕适配】浅析px、dp、ppi、dpi、sp

    iOS开发中使用的单位pt与ps中的pt是不是同一个概念?个人觉得不是。望高手解答…?

    腾讯何家成-Android 屏幕适配:最全面的解决方案

    张鸿洋-Android 屏幕适配方案

    简书-一种非常好用的Android屏幕适配—smallestWidth 限定符适配

    2014年Android碎片化报告

    约束布局ConstraintLayout看这一篇就够了

    译-带你领略 ConstraintLayout 1.1 的新功能
    深入Android大图片处理机制

    展开全文
  • Android设备App设计中有一个问题可能会被设计师忽略,在各种分辨率各种尺寸“杂屏”的界面适配。可能产出的界面稿在常用的720*1280的分辨率中是完美,但一到各个不同分辨率不同尺寸的设备后这里就谈谈适配,了解适配...

    Android设备App设计中有一个问题可能会被设计师忽略,在各种分辨率各种尺寸“杂屏”的界面适配。可能产出的界面稿在常用的720*1280的分辨率中是完美,但一到各个不同分辨率不同尺寸的设备后

    这里就谈谈适配,了解适配让设计从PS、sketch到移动设备上都能完美呈现。

    设备

    如此繁杂的安卓设备,采用哪个标准设计呢?

    1.选择一种尺寸一种分辨率作为基准。

    2.选择2-3款主流的Android设备,制定一套适配规则。(国内主流设备、分辨率可参考友盟指数)

    3.部分极端效果特别注释说明。

    目前移动端设计师多采用iPhone 5与6的分辨率设计,这两个分辨率也最接近Android xhdpi的720*1280,设计之后再做等比适配(不做设计元素等比适配会导致Android设备上视觉呈现较小)。

    我则倾向于选取720*1280的分辨率设计。优点是处于常用分辨率的中间值,对小分辨率大分辨率调整也较容易。另外iOS@1x的320与720刚好是2.25的倍率关系,使用sketch等比输出快捷多了。(如果时间成本允许的话可以将Android的标注单位用dp,具体的设备尺寸、分辨率知识这里不详描述,可见文章最后面的“Android基础知识”)

    案例说明:

    雅虎新闻为各个dpi做了优化,图片等比缩放,文字区域等比缩放,并且考虑到在低dpi下会被推移至第二屏,就减小图片了高度,保持文字区域最小高度。

    9a0a3fb7e0f7

    雅虎新闻

    图标

    老司机都不会忘记的,仅提醒下新手,每个图标记得输出多个比例。并且记得查看各个比例下图标的显示效果。

    案例说明:

    还是拿一个雅虎新闻的例子,大家感受下。

    9a0a3fb7e0f7

    雅虎新闻icon

    文本

    Android设备的系统各个厂商都做了定制化,默认的字体库可能不同,且字体占空间大小可能不同。不同设备显示文字会出现不同效果。设计时考虑3点:

    多采用流式布局,不对单行做字数限制(如“单行显示多少个字”“文本宽度多少”),而是定义文本容器的高宽,超出则用“…”“渐隐”或者“遮挡”等方式省略。

    若较长的文本需要完整显示,设计时预留换行空间。

    若文本需要在单行完整显示(如提示类文字),尽量控制字数(建议16字内),避免小屏不够放置。

    9a0a3fb7e0f7

    案例说明:

    图文混排同一行显示时,图片等比固定在右侧显示,文字部分区域宽度会因设备不同出现较大的差异,预留文字多行高度。如下图不同设备下文字的展示空间有差异,需要考虑小分辨率下预留多行文字空间。如图2第二条新闻标题文字溢出的丑陋展示,建议设定一个文字区域最大高度,超出部分则隐藏。

    9a0a3fb7e0f7

    网易游戏App

    单行出现多个文字元素时,注意元素在低dpi下的显示层级,提前说明好该情况的覆盖或者隐藏规则。如下图第一个用户名称,在低dpi下,避免各元素交错,而省略了超出的用户名称。

    9a0a3fb7e0f7

    网易游戏App

    图片

    图片常用的方式有固定宽度dp等比缩放高度(用于非通栏图片);固定高度dp等比缩放宽度(用于横向滚动图片,如全屏相册中的纵向图片);根据屏幕宽度等比缩放(横向通栏图片)。设计时考虑3点:

    注意图片占用的宽高比,避免大屏设备上占据大量空间,导致内容比例不协调同时降低了屏幕利用率。

    考虑到设备屏幕密度不同,输出图片时别忘了输出多个分辨率。

    考虑图片宽高比过大的缩略图处理(最常见的处理方式:高度远大于宽度时,是给出最大区域,让图片等宽居中填充该区域,只显示该区域;宽度远大于高度时,与展示区域等高居中取部分显示。当然也可能出现特殊显示要求,需要根据具体情况具体处理。)

    9a0a3fb7e0f7

    案例说明:

    网易游戏相册的全屏浏览中,大于设备宽高比的宽图按照最大宽度放置,小于设备宽高比的高度按照最大高度放置。

    9a0a3fb7e0f7

    网易游戏App

    一行多张图片要考虑图片的在不同设备下等比缩放带来显示效果的差异。排列时会有两种情况:

    1.要求在一行内显示完,根据图片的显示效果决定放置的数量,超过则不显示(如下图1第二条新闻)

    2.流式布局,当图片宽度小于设定值时自动换行(如下图2相册展示,低dpi低分辨率设备一行显示3张,高的显示4-5张,且按比例撑满屏幕宽度)。

    9a0a3fb7e0f7

    网易游戏App

    宽高比超出设计区域时的处理,如百度贴吧中列表的小图模式,给出了正方形区域,当图片非正方形时,根据宽高中的短边等比撑满正方形区域后,截取了图片居中的部分显示。

    9a0a3fb7e0f7

    百度贴吧

    区域

    在固定区域内多元素混合放置时,文字一般采取流式布局,图片多采用等比缩放,图标元素多采用 弹性布局,即元素内容本身规格不变,考虑水平、垂直方向的间距做相应扩展。设备屏幕越大,在扩展方向上可以显示更多内容,发挥了大屏幕的优势。

    弹性布局需要给出哪一个元素dp不变,哪一个元素缩放的策略。

    弹性布局下部分距离标注采用百分比标注。

    当有两个等比缩放元素时考虑避免重叠的情况。

    9a0a3fb7e0f7

    案例说明:

    网易游戏的新闻列表样式,每一条新闻区域,要求图片dp不变,而文字区域进行弹性缩放。

    9a0a3fb7e0f7

    网易游戏App

    下图网易游戏专区中间的幽灵按钮图标为确保点按效果,按照固定dp显示,中间间隔的宽度按照设备宽度的百分比来确定

    9a0a3fb7e0f7

    网易游戏App

    网易游戏求交往的界面,中间卡片区域大小根据设备等比缩放,如中间用户头像与“同喜欢2款游戏”的文字,在设计时需要考虑产品的目标设备中最小设备下的布局显示效果,避免出现重叠的情况。而纵向的元素数量也需要如此考虑。

    9a0a3fb7e0f7

    网易游戏App

    Android界面适配的案例说明就写到这里啦。

    设计时多考虑各个元素(图标、文本、图片、区域)在不同设备的情况。当然,做设计时也不是死板的按照建议来实现,特别是固定区域下的元素放置,根据实际情况来处理即可。

    Android系统的UI也在不断进化完善,随着设计趋势的改变,Android除了常见的卡片、列表、浮层外,可能会有更多的展示方式,而Android设备也是逐渐淘汰ldpi与mdpi,设备的分辨率逐渐变大。也就要求我们需要不断的去了解尝试新的设计趋势,产出最好的方案。

    附录:Android基础知识

    这不是彩蛋哈,仅仅补充安卓设备的基础知识,老司机完全可以忽略,供新手参考阅读。

    为展示设备的多样化,贴出Android屏幕尺寸示意图(蓝色矩形的大小代表不同尺寸,颜色深浅则代表所占百分比的大小。)

    9a0a3fb7e0f7

    屏幕大小(Screen Size)

    屏幕大小以屏幕尺寸来衡量,指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米

    目前的主流尺寸:5.0" ~ 5.5" (有继续往更大尺寸发展的趋势,但趋于稳定)

    常见的设备尺寸: 4" ~ 10" 。

    手机适配参考尺寸: 4" ~ 6"

    手机 + 平板适配参考尺寸: 4" ~ 10”

    屏幕分辨率(Screen Resolution)

    屏幕分辨率是指在横纵向上的像素点数,单位是px,1px=1个像素点。一般以纵向像素*横向像素,如1960*1080。

    屏幕密度(Screen Density)

    屏幕像素密度是指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写。目前每个屏幕像素可以认为就是一个“点”。

    屏幕 dpi 的计算方式:

    9a0a3fb7e0f7

    Android 设备中 dpi 分几个段:

    •ldpi:~ 120 dpi (几乎绝迹)

    •mdpi:~ 160 dpi (罕见)

    •hdpi:~ 240 dpi (逐渐减少中)

    •xhdpi:~ 320 dpi

    •xxhdpi:~ 480 dpi

    •xxxhdpi:~ 640dpi (目前较少)

    设备独立像素(dp/dip:device independent pixels)

    dp(与 dip 同义) 是在 160dpi 下每个像素对应的物理尺寸,可近似理解为:

    •160 dp = 1 inch

    •1 dp = 1 / 160 inch = 0.15875 mm

    •1 dp = 1 px (160 dpi 屏幕下)

    •1 dp = 2 px (320 dpi 屏幕下)

    Android的屏幕适配指标都基于物理尺寸(即屏幕的物理大小),而非像素(分辨率)。为什么呢?这里根据dp与px适配出两种效果来说明。

    按 dp 适配不同屏幕的效果如下,内容的物理尺寸变化不大:

    9a0a3fb7e0f7

    若直接按照像素适配,出现以下情况,高像素密度的设备内容显得特别小,影响布局与可用性:

    9a0a3fb7e0f7

    屏幕比例

    屏幕长边和短边的比例。

    目前手持设备的 长边 dpi 和 短边 dpi 普遍非常接近,可认为屏幕比例和屏幕水平、垂直像素比例一致

    屏幕比例目前趋于 16:9 ~ 16:10 (8:5)

    因不少设备使用了虚拟按键,所以通常非全屏的 app 可用面积略低,屏幕比例更接近 16:10

    9a0a3fb7e0f7

    展开全文
  • 这是自己项目中用的界面适配工具,通过获取当前设备的分辨率来做等比例缩放的工具。 下面是两种常用的地方与方式 1.BaseActivity方式,通过集成基类来对activity的界面做适配 package com.jiuma.projectbase; ...

    这是自己项目中用的界面适配工具,通过获取当前设备的分辨率来做等比例缩放的工具。

    下面是两种常用的地方与方式

    1.BaseActivity方式,通过集成基类来对activity的界面做适配

    package com.jiuma.projectbase;
     
    import android.app.Activity;
    import android.util.DisplayMetrics;
    import android.view.View;
    import android.view.ViewGroup.LayoutParams;
     
    public class BaseActivity extends Activity {
    	/******** 基准分辨率 **********/
    	private static final float UI_STANDARD_width = 720;
    	/******** 缩放比例值 **********/
    	protected static float scale = 0;
     
    	@Override
    	public void setContentView(int layoutResID) {
    		View view = View.inflate(this, layoutResID, null);
    		this.setContentView(view);
    	}
     
    	@Override
    	public void setContentView(View view) {
    		if (scale == 0) {
    			initScreenScale();
    		}
     
    		if (scale != 1) {
    			RelayoutTool.relayoutViewHierarchy(view, scale);
    		}
    		super.setContentView(view);
    	}
     
    	@Override
    	public void setContentView(View view, LayoutParams params) {
    		if (scale == 0) {
    			initScreenScale();
    		}
    		RelayoutTool.relayoutViewHierarchy(view, scale);
    		RelayoutTool.scaleLayoutParams(params, scale);
    		super.setContentView(view, params);
    	}
     
    	public void initScreenScale() {
    		DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
    		float width = displayMetrics.widthPixels;
    		scale = width / UI_STANDARD_width;
    	}
    }
    

    2.单独的View-Adapter中getView适配等等

    View convertView =LayoutInflater.from(context).inflate(R.layout.view_item_auto_type_list, null);;
         RelayoutTool.relayoutViewHierarchy(convertView, scale);
    

    下面是RelayoutTool

    package com.jiuma.projectbase;
     
    import java.lang.reflect.Field;
     
    import android.util.TypedValue;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.ViewGroup.LayoutParams;
    import android.view.ViewGroup.MarginLayoutParams;
    import android.widget.TextView;
     
    /**
     * 
     * 重新布局工具类, 迭代的形式将视图层级按比例缩放;
     * 
     * @author {YueJinbiao}
     */
    public class RelayoutTool {
     
    	/**
    	 * ForW4H3 ForW16H9 将原视图 宽高,padding,margin, 及文本字体大小 按比例缩放,重新布局;
    	 * 
    	 * @param view
    	 *            单个视图,或视图层级
    	 * @param scale
    	 *            缩放比例
    	 */
    	public static void relayoutViewHierarchy(View view, float scale) {
     
    		if (view == null) {
    			return;
    		}
     
    		scaleView(view, scale);
     
    		if (view instanceof ViewGroup) {
    			View[] children = null;
    			try {
    				Field field = ViewGroup.class.getDeclaredField("mChildren");
    				field.setAccessible(true);
    				children = (View[]) field.get(view);
    			} catch (NoSuchFieldException e) {
    				e.printStackTrace();
    			} catch (IllegalArgumentException e) {
    				e.printStackTrace();
    			} catch (IllegalAccessException e) {
    				e.printStackTrace();
    			}
    			if (children != null) {
    				for (View child : children) {
    					relayoutViewHierarchy(child, scale);
    				}
    			}
    		}
    	}
     
    	/**
    	 * 将视图按比例缩放,不考虑嵌套视图;
    	 * 
    	 * @param view
    	 *            不考虑嵌套,缩放单个View;
    	 * @param scale
    	 *            缩放比例;
    	 */
    	private static void scaleView(View view, float scale) {
     
    		if (view instanceof TextView) {
    			resetTextSize((TextView) view, scale);
    		}
     
    		int pLeft = convertFloatToInt(view.getPaddingLeft() * scale);
    		int pTop = convertFloatToInt(view.getPaddingTop() * scale);
    		int pRight = convertFloatToInt(view.getPaddingRight() * scale);
    		int pBottom = convertFloatToInt(view.getPaddingBottom() * scale);
    		view.setPadding(pLeft, pTop, pRight, pBottom);
    		LayoutParams params = view.getLayoutParams();
    		scaleLayoutParams(params, scale);
    	}
     
    	/**
    	 * 将视图布局属性按比例设置;
    	 * 
    	 * @param params
    	 * @param scale
    	 *            缩放比例;
    	 */
    	public static void scaleLayoutParams(LayoutParams params, float scale) {
    		if (params == null) {
    			return;
    		}
    		if (params.width > 0) {
    			params.width = convertFloatToInt(params.width * scale);
    		}
    		if (params.height > 0) {
    			params.height = convertFloatToInt(params.height * scale);
    		}
     
    		if (params instanceof MarginLayoutParams) {
    			MarginLayoutParams mParams = (MarginLayoutParams) params;
    			if (mParams.leftMargin > 0) {
    				mParams.leftMargin = convertFloatToInt(mParams.leftMargin
    						* scale);
    			}
    			if (mParams.rightMargin > 0) {
    				mParams.rightMargin = convertFloatToInt(mParams.rightMargin
    						* scale);
    			}
    			if (mParams.topMargin > 0) {
    				mParams.topMargin = convertFloatToInt(mParams.topMargin * scale);
    			}
    			if (mParams.bottomMargin > 0) {
    				mParams.bottomMargin = convertFloatToInt(mParams.bottomMargin
    						* scale);
    			}
    		}
    	}
     
    	/**
    	 * 将 TextView(或其子类)文本大小按比例缩放;
    	 * 
    	 * @param textView
    	 * @param scale
    	 *            缩放比例;
    	 */
    	private static void resetTextSize(TextView textView, float scale) {
    		float size = textView.getTextSize();
    		 textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size * scale);
    	}
     
    	/**
    	 * float 转换至 int 小数四舍五入
    	 */
    	private static int convertFloatToInt(float sourceNum) {
    		return (int) (sourceNum + 0.5f);
    	}
     
    }
    

    转载地址:https://blog.csdn.net/u013069608/article/details/53884753?utm_term=android%E5%B8%83%E5%B1%80%E7%AD%89%E6%AF%94%E4%BE%8B%E7%BC%A9%E6%94%BE&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allsobaiduweb~default-5-53884753&spm=3001.4430

    展开全文
  • Android 11适配攻略

    2021-08-18 19:32:02
    2020年6月11日,Google正式推送了Android 11 Beta版本,同年年9月9日正式发布。系统主要增强了聊天气泡,安全性和隐私性的保护,电源菜单,可以更好的支持瀑布屏,折叠屏,双屏和 Vulkan 扩展程序等
  • Android屏幕适配

    2021-06-05 17:16:50
    为什么要进行Android屏幕适配由于Android系统的开放性,任何用户、开发者、OEM厂商、运营商都可以对Android进行定制,于是导致:1、Android系统碎片化:小米定制的MIUI、魅族定制的flyme、华为定制的EMUI等等2、...
  • android适配方案

    2021-05-27 06:30:32
    一, 关于适配的一些概念性的知识1.1. 重要的概念解释1.1.1. 屏幕尺寸:屏幕尺寸指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米比如常见的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等图片1.png1.1.2.屏幕...
  • 在我们学习如何进行屏幕适配之前,我们需要先了解下为什么Android需要进行屏幕适配。由于Android系统的开放性,任何用户、开发者、OEM厂商、运营商都可以对Android进行定制,修改成他们想要的样子。但是这种“碎片化...
  • 前言Android设计之初就考虑到了UI在多平台的适配,它本身提供了一套完善的适配机制,随着版本的发展适配也越来越精确,UI适配主要受平台两个因素的影响:屏幕尺寸(屏幕的像素宽度及像素高度)和屏幕密度,针对不同的...
  • Android UI适配方案

    2021-05-26 08:16:13
    UI适配方案Android的ui适配一般有两种解决方案,一种是通过系统的资源适配机制(layout和drawable),另一种是通过代码来适配(fragment和view),两种方案也经常结合起来用。1 资源适配Android的资源适配机制的原理就是...
  • android机型适配终极篇

    2021-06-10 16:45:02
    分辨率就是手机屏幕的像素点数,一般描述成屏幕的“宽×高”,安卓手机屏幕常见的分辨率有480×800、720×1280、1080×1920等。720×1280表示此屏幕在宽度方向有720个像素,在高度方向有1280个像素。(2)屏幕大小。...
  • IOS与Android UI适配方案

    2021-06-06 18:16:59
    方案一IOS与Android共用一套效果图 1242*2208IOS与Android常用的尺寸中,最大尺寸的为i6+的尺寸,即1242*2208pxIOS常用尺寸为1242*2208750*1334640*1136640*960其中750*1334640*1136640*960同为@2x,1242*2208为@3x...
  • 在需要适配的地方使用引用,在不同的最小屏幕和像素密度的文件夹里面创建一份对应的尺寸。例如:mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi, sw320dp-hdpi, sw320dp-xxhdpi, sw360dp-hdpi, sw411dp-xxhdpi, w360dp, h360dp...
  • Android屏幕适配(一)--自定义View屏幕适配Android屏幕适配(一)--自定义View屏幕适配同一个界面需要在不同尺寸的屏幕上显示,即使屏幕的尺寸一样,密度也可能不一样。导致App的界面元素在不同屏幕尺寸上显示不一致。...
  • 前言Android适配是一个老生常谈的问题,很多程序员觉得很恶心,不愿意做适配,但是又不得不做。然后老板说,这位兄弟,做好了,今天晚饭给你加个鸡腿,然后程序员开始找各种资料,忙活起来了,最终在苦逼的煎熬中做...
  • 适配步骤1、用Android模式打开项目,在res文件新建文件夹values-960x540 和文件夹values-1500x12002、在project模式下打开项目,在values-960x540 新建dimens.xml文件,在里面创建dimen标签,代码如下200dp3、在proj...
  • Android 12应用适配指南

    千次阅读 2021-05-20 15:05:27
    Android 12应用适配指南1.Android 12上的主要变更1.1 兼容性1.1.1 前台服务启动限制1.1.2 前台服务通知延迟1.1.3 待处理 intent 必须声明可变性1.1.4 非SDK接口名单更新1.2 用户体验1.2.1 接收内容的统一API1.2.2 ...
  • Android屏幕适配(一)

    2021-06-03 08:41:28
    为什么要屏幕适配由于Android系统的开放性,任何用户、开发者、OEM厂商、运营商都可以对Android进行定制,于是导致运行 Android 的设备多种多样,它们有着不同的屏幕尺寸和像素密度。 尽管系统可通过基本的缩放和...
  • Android字体的适配问题

    2021-02-07 14:56:53
    很明显,当Android系统设置了超大字体的时候,你的应用里设置的sp字体就会跟随系统变得很大,导致界面混乱有两个解决办法。 1.Activity设置字体 我们可以在BaseActivity中设置 @Override public Resources ...
  • 新项目,做平板适配,看了网上的很多适配方案,感觉都不合适,最后,干脆用今日头条适配.经几个月的临床经验,果然能用,但是要注意几个问题:(顺便说一下,如果身边有丝袜,短裙,哈哈哈哈,好姑娘.... 记得给我介绍一下,当然,...
  • 本文将带你了解Android应用开发Android Studio适配利器 怎样设置不同机型的预览界面方法,希望本文对大家学Android有所帮助。"对于Android开发者来说,适配是一大难题。对于我们公司而言,一款APP需要适配好几款不同...
  • 关于这款手机背后的黑科技,相信大家都已经有所了解,我们就不再展开讨论,对于开发者来说,最关心的莫过于折叠屏的适配问题,对此,我们联系到了华为终端的相关技术专家,为大家获取了折叠屏的官方适配方案,以下.....
  • Android 版本适配

    2021-06-04 06:45:42
    8.0 Android PtargetSdkVersion改为 281.Http请求失败在9.0中默认情况下启用网络传输层安全协议 (TLS),默认情况下已停用明文支持。也就是不允许使用http请求,要求使用https。比如我使用的是okhttp,会报错:java...
  • Android适配的那些P事

    2021-05-27 01:00:04
    声明:本文从设计角度说明设计稿及倍图适配问题,不含前端技术,码农慎入首先我们看看...实操过安卓适配的同学应该了解,并不是所有的生厂商都会做规规矩矩的屏幕分辨率;单位解释篇想要通透得了解关于安卓各机型的...
  • Android 12 快速适配要点

    千次阅读 2021-12-02 22:12:21
    Android 12 需要更新适配点并不多,本篇主要介绍最常见的两个需要适配的点:android:exported 和 SplashScreen 。 一、android:exported 它主要是设置 Activity 是否可由其他应用的组件启动, “true” 则表示可以,...
  • Android N及以上版本应用安装包下载完成自动弹出安装界面适配方法在实现下载和安装APP功能的时候在Android较高版本可能会遇到如下的问题:安装Apk时报错:android.os.FileUriExposedException: file:///storage/...
  • Android适配全攻略(学习笔记总结)一、为什么要进行屏幕适配某厂商统计如下数据2012年,支持Android的设备共有3997种2013年,支持Android的设备共有11868种2014年,支持Android的设备共有18796种2015年,支持Android...
  • 背景之前基于头条的适配方案写了篇文章 Android 屏幕适配从未如斯简单,但后续发现还是有挺多坑的,这些坑都记录在了 GitHub 屏幕适配问题汇总及解决,基于这么多坑,最终我发现了一种更完美的适配方案,本打算一个...
  • 一、开始之前在unity打包的apk中,一般来说,调用Android方式,通过类似obj = new AndroidJavaObject("android.content.Intent")obj .Call("方法名", 参数);这样的方式调用来实现一些简单的功能,但是当实现比较复杂...
  • 背景:众所周知,国内的android是在google之上,各大手机厂商加了一层,诸如MIUI、EUI,之类的系统。因此在我们开发过程中想调出某应用的权限设置页面,调出的是google原生的权限控制界面,但是我们在此页授予应用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,635
精华内容 15,054
关键字:

安卓界面适配