Android屏幕适配二

2018-10-23  本文已影响0人  Hengtao24

系列文章

前言

从 Android 1.6(API 4)开始,Android支持多种屏幕尺寸和密度,因此Android屏幕适配需要做到两个方面,尺寸适配和密度适配,确保应用正常渲染,在每个屏幕上提供最佳的用户体验。

在前文Android屏幕适配(一),相关概念中,主要给出了一些概念的定义,本文具体说明屏幕适配的解决办法。

Android屏幕适配问题及解决方案

屏幕尺寸适配方案

布局适配

1.使布局自适应屏幕尺寸

使用相对布局,不要使用绝对布局(AbsoluteLayout)

与其他布局不同,AbsoluteLayout 会强制使用固定位置放置其子视图,很容易导致在不同显示屏上显示效果不好的用户界面。因此,AbsoluteLayout 在 Android 1.5(API 级别 3)上便已弃用。

使用RelativeLayout则会使用相对位置来布局子控件,即使屏幕大小改变,视图之间的位置也不会改变。

LinearLayout的嵌套实例和"wrap_content"尺寸与 "match_parent"尺寸的组合可以构建相当复杂的布局。不过LinearLayout不允许精确控制子视图的空间关系;LinearLayout 中的视图只是排成一行。

例如:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/label"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Type here:"/>
    <EditText
        android:id="@+id/entry"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/label"/>
    <Button
        android:id="@+id/ok"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/entry"
        android:layout_alignParentRight="true"
        android:layout_marginLeft="10dp"
        android:text="OK" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@id/ok"
        android:layout_alignTop="@id/ok"
        android:text="Cancel" />
</RelativeLayout>

该布局在QVGA(240x320,ldpi)屏幕上的外观显示:


QVGA 屏幕(小屏幕)上的屏幕截图

2.根据屏幕配置加载相应布局

使用尺寸限定符

我们的应用不仅应该实现灵活布局,还应针对不同的屏幕配置提供多种备选布局,可以通过配置限定符实现,它允许组件根据当前设备配置(如针对不同屏幕尺寸的不同布局设计)自动选择合适的资源。

例如:许多应用都针对大屏幕实现了“双窗”模式(应用可以在一个窗中显示项目列表,在另一个窗中显示项目内容)。 平板电脑和 TV 足够大,可在一个屏幕上同时容纳两个窗,但手机屏幕只能独立显示它们。因此,如需实现这些布局,我们可以编写以下布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="match_parent" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

但这种方式只适合Android3.2之前

使用最小宽度限定符(Smallest-width)

在Android3.2以下版本设备中,5寸和7寸设备都被视为“大屏”设备,但是许多应用可能需要精确的根据屏幕配置加载布局文件,所以Android3.2中引入了最小宽度限定符。

最小宽度限定符通过指定某个特定最小宽度(单位:dp)来加载不同的UI资源。

例如,典型的7英寸平板电脑最小宽度为600dp,因此,如果希望UI在这些屏幕上显示双窗(但在较小屏幕上显示单个列表),我们使用sw600dp为最小宽度是600dp的屏幕指定双窗布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="match_parent" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

对于宽度≥600dp的设备将选择layout-sw600dp/main.xml(双窗)布局,而屏幕较小的设备将选择 layout/main.xml(单窗)布局。

使用布局别名

最小宽度限定符只在Android3.2及以上版本有效,若要兼容早期版本,例如在手机上显示单窗,在大屏设备上显示双窗,则需要编写以下布局文件:

后两个文件内容完全一致,只是文件名不一样,分别支持不同版本的Android设备。为了避免这种重复内容的文件(避免后期维护中的问题),我们可以使用文件别名,定义以下布局:

并添加两个文件:

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

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

使用屏幕方向限定符

假设某一应用布局在各种屏幕尺寸和屏幕方向下的行为如下:

因此,将每一种布局定义在res/layout/目录下的某个XML文件,使用应用别名将对应布局分配给各种屏幕配置。共定义四种布局:(具体代码见Android开发者官网)

res/layout/onepane.xml
res/layout/onepane_with_bar.xml
res/layout/twopanes.xml
res/layout/twopanes_narrow.xml

再使用布局别名:(具体代码见Android开发者官网)

res/values/layouts.xml
res/values-sw600dp-land/layouts.xml
res/values-sw600dp-port/layouts.xml
res/values-large-land/layouts.xml
res/values-large-port/layouts.xml

匹配的过程和最小限定符类似,不再赘述。

视图组件适配

对布局尺寸使用 wrap_content、match_parent

为了使布局能灵活适应不同屏幕尺寸,某些视图组件的宽度和高度应该使用"wrap_content""match_parent"

为 XML 布局文件中的视图定义android:layout_widthandroid:layout_height时,使用 "wrap_content""match_parent" 可确保在当前设备屏幕上为视图提供适当的尺寸。

图片资源适配

使用九图(Nine-Patch)

支持不同屏幕尺寸通常意味着图片资源也必须能够适应不同的尺寸。例如,一个Button的背景图片必须能够适应其大小变化。

如果我们对该Button使用普通的位图,会发现效果很差,因为Button会均匀地拉伸或缩小图像。

解决方案是使用九宫格位图,这种特殊格式的 PNG (后缀名是:.9.png)文件会指示哪些区域可以拉伸,哪些区域不可以拉伸。

9图的制作教程这里暂时不说,网上教程也比较多。(参考Android开发者官网)

屏幕密度适配方案

视图组件适配

使用密度无关像素

设计布局需要避免用绝对像素来定义距离或者尺寸,因为不同屏幕具有不同像素密度,同样像素数量在不同设备上可能对应不同物理尺寸。因此,设计布局文件时务必使用dpsp单位(具体定义参考Android屏幕适配(一),相关概念 )。

layout_width="100dp"的视图在中密度屏幕上测出宽度为100像素,在高密度屏幕上系统会将其扩展至150像素宽, 因此视图在屏幕上占用的物理空间大约相同。例如:指定两个视图间距:

<Button android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/clickme"
    android:layout_marginTop="20dp" />

类似地,选择sp来定义文本大小。sp缩放系数取决于用户设置,系统会像处理dp一样缩放大小,但切勿为布局尺寸使用该单位。例如:

<TextView android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="20sp" />

在Android中,规定以160dpi(即屏幕分辨率为320x480)为基准:1dp=1px。

图片资源匹配

Android的设备具有多种屏幕密度,我们需要提供能够根据各种通用密度级别(ldpi,mdpi,hdpi,xhdpi)进行定制的图片资源。

如需生成这些图像,我们以原始资源为基础,按以下尺寸缩放比例生成每种屏幕密度对应的图像:

如果为 xhdpi 设备生成了一幅 200x200 的图像,则应分别按 150x150、100x100 和 75x75 图像密度为 hdpi 设备、mdpi 设备和 ldpi 设备生成同一资源。

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

MyProject/
  res/
    drawable-xhdpi/
        awesomeimage.png
    drawable-hdpi/
        awesomeimage.png
    drawable-mdpi/
        awesomeimage.png
    drawable-ldpi/
        awesomeimage.png

百分比布局

前端设计中,网页提供了百分比计算,从而可以根据百分比来设计布局。控件的宽度可以去参考父控件的宽度去设置百分比,最外层控件的宽度参考屏幕尺寸设置百分比。

谷歌曾开源了一个支持百分比布局的项目android-percent-support-lib-sample,但现在该项目已经被废弃,取而代之推荐我们使用ConstraintLayout 布局方式,具体可以参考郭霖大神的Android新特性介绍,ConstraintLayout完全解析这篇文章。

总结

由上述的各种适配解决方案,我们可以总结如下:通过提供各种布局文件,各种图片资源,合理使用九图,布局限定符等,以dp为核心对不同的屏幕适配。正确的理解核心概念是正确适配的关键。

参考信息

上一篇 下一篇

猜你喜欢

热点阅读