提高应用兼容性之屏幕适配

2020-05-06  本文已影响0人  tandeneck

背景

做 Android 开发的都知道,Android 设备的碎片化太严重了,由此产生的屏幕兼容性问题令人秃头。不过兵来将挡水来土掩,本文就来撸一撸屏幕适配方法,具体说来有:

使用 ConstraintLayout

使用 ConstraintLayout,可以根据布局中 View 之间的空间关系来指定每个 View 的位置和大小,当屏幕尺寸改变时,所有 View 都可以一起移动和拉伸,还可以减少布局层级,何乐而不为?不过不建议在 RecyclerView 的 item 中使用,会有性能问题。

避免使用硬编码的布局尺寸

为了确保布局能够灵活地适应不同的屏幕尺寸,尽量使用 wrap_content 和 match_parent,而不是硬编码的尺寸。如果使用 LinearLayout,则可以设置 layout_weight,以便每个 View 按照自身权重值所占的比例填充剩余的空间。

创建备用布局

虽然布局可以通过拉伸或者相对位置来适配不同的屏幕尺寸,但体验并不是很好,比如本来在手机状态表现良好的布局,在平板上惨不忍睹。因此,应该提供备用布局资源,以针对特定屏幕尺寸优化设计。具体做法可以通过创建额外的 res/layout/ 目录来提供特定于屏幕的布局(针对需要不同布局的每种屏幕配置创建一个),然后将屏幕将屏幕配置限定符附加到 layout 目录名称(例如,对于可用宽度 600dp 的屏幕,附加限定符 layout-w600dp)。

使用最小宽度限定符(不考虑屏幕方向)

最小宽度屏幕尺寸限定符允许为具有最小宽度(dp为单位)的屏幕提供备用布局。

    res/layout/main_activity.xml         #小于 600dp 的设备
    res/layout-sw600dp/main_activity.xml   #大于大于 600dp 的设备

最小宽度限定符指定屏幕两侧的最小尺寸,而不考虑当前的屏幕方向,因此这是一种指定布局可用的整体屏幕尺寸的简单方法。以下是最小宽度值与典型屏幕尺寸的对应关系:

使用可用宽度限定符(考虑屏幕方向)

有时候需要根据当前可用的宽度或高度来更改布局,而不是根据屏幕的最小宽度来更改布局,因为屏幕宽度可能会根据设备的屏幕方向是横向还是纵向而发生变化。在这种情况下,应该使用可用宽度限定符来执行同样的操作。

    res/layout/main_activity.xml         # 当前宽度小于 600dp
    res/layout-w600dp/main_activity.xml  # 当前宽度大于等于 600dp

当然如果关注的是当前可用高度,则使用可用高度限定符执行同样的操作,例如:对于当前屏幕高度至少为 600 dp 的屏幕,使用限定符 layout-h600dp。

添加屏幕方向限定符

有时候比较关心的是页面在竖屏和横屏之间的表现,可以将 port 或 land 限定符添加到资源目录名称。只是需要确保这些限定符在其他尺寸限定符后面:

    res/layout/main_activity.xml                # 手机
    res/layout-land/main_activity.xml           # 手机横屏
    res/layout-sw600dp/main_activity.xml        # 平板
    res/layout-sw600dp-land/main_activity.xml   # 平板横屏

使用 Fragment 将页面组件模块化

典型例子就是新闻应用了,在平板电脑上可以在左侧使用一个 Fragment 显示报道列表,而在右侧使用另一个 Fragment 显示一篇完整的报道,它们都添加到一个容器中一般是 Activity。而在手机上,这两个 Fragment 显示在独立的容器中。

创建可拉伸的九宫格位图

如果在改变尺寸的 View 中使用 Bitmap 作为背景,系统会根据设备不同而缩放图片,导致模糊或者图像失真。这时就可以使用 .9.png 啦,即点九图。

使用密度无关像素(dp)设计页面

Android 设备不仅有不同的屏幕尺寸,而且其屏幕也有不同的像素尺寸。因此应该避免直接用像素(px)来定义距离和尺寸,而应该使用 dp。为了更好的理解,下面对一些基本概念进行说明:

日常开发中,比较关注的是 dp 和 px 之间的转换:

   //px 转成 dp
  public static int px2dip(Context context, float pxValue) {  
       final float scale = context.getResources().getDisplayMetrics().density;  
       return (int) (pxValue / scale + 0.5f);  // int 向下取整,加0.5 是实现四舍五入
  }  
   
  //dp 转成 px
  public static int dip2px(Context context, float dipValue) {  
       final float scale = context.getResources().getDisplayMetrics().density;  
       return (int) (dipValue * scale + 0.5f);  
  }

使用系统的预配置值

推荐使用 ViewConfiguration 类来获取 Android 系统常用的距离、速度和时间,例如可通过 getScaledTouchSlop() 方法获取系统用作滚动阈值的距离(以像素为单位):

private static final int GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).getScaledTouchSlop();

ViewConfiguration 中以 getScaled 为前缀的方法都会返回以像素为单位的值,无论当前像素密度是多少,该值都会正确显示。

将图片资源放置在合适的目录下

不过日常开发中设计师一般也只给 3 倍图,我们把其放在 xxhdpi 目录下即可,这是综合了减少包体积和显示效果等因素后的结果。

将应用图标放在 mipmap 目录中

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

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

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

拓展:

结束语

以上就是关于屏幕适配的一些通用方法啦。

上一篇下一篇

猜你喜欢

热点阅读