问题
Android 设备各种屏幕尺寸和形状,如何做好适配?
你还在为开发中频繁切换环境打包而烦恼吗?快来试试 Environment Switcher 吧!使用它可以在app运行时一键切换环境,而且还支持其他贴心小功能,有了它妈妈再也不用担心频繁环境切换了。https://github.com/CodeXiaoMai/EnvironmentSwitcher
解决思路和办法
- Android 系统定义了两种常规属性对设备屏幕进行分类:大小和密度。
- 为了优化应用程序的外观,以适应不同的屏幕尺寸和密度,可以添加一些替代资源(布局和图片)。
因此对不同屏幕尺寸、形状以及密度的适配,其实就是对布局和图片的适配。
布局适配
要优化不同屏幕尺寸的用户体验,应该为要支持的每个屏幕尺寸创建一个独特的布局XML文件。每个布局应该保存到相应的资源目录中,命名为 - <screen_size>后缀。
Android 3.2 之前和之后的方案是不一样的。
Android 3.2 -
系统定义了四种常用大小:small, normal, large, xlarge
和四种常用密度:low (ldpi), medium (mdpi 默认), high (hdpi), extra high (xhdpi)
就像适配不同的语言一样,要声明用于不同屏幕的布局和位图,必须将这些替代资源放在不同的目录中。
还要注意,屏幕方向(横向或纵向)也被认为是屏幕尺寸的变化,所以许多应用程序应该修改布局,以优化每个方向的用户体验。
例如,大型屏幕的独特布局应该保存在 res/layout-large/ 下。
Android会自动缩放布局,以适应屏幕。因此,不同屏幕尺寸的布局不需要担心UI元素的绝对大小,而需要重点关注的是影响用户体验的布局结构(例如相对于兄弟视图的重要视图的大小或位置)。
例如,在项目中创建 默认布局
和 支持大屏幕
的替代布局。
MyProject/
res/
layout/
main.xml
layout-large/
main.xml
如上面代码所示,文件名必须完全相同,但内容不同,才能为相应的屏幕尺寸提供优化的UI。
系统会根据运行应用程序的设备的屏幕大小从相应的布局目录加载布局文件。有关Android选择适当资源的更多信息,请参阅“提供资源”指南。
如果应用支持横竖屏切换,还应提供横屏的布局,这里是一个具有用于横向定向的替代布局的项目
MyProject/
res/
layout/
main.xml
layout-land/
main.xml
默认情况下,layout/main.xml
文件用于纵向。
如果你想提供一个支持 大屏幕
和 横屏
的特殊布局,你需要同时使用 large
和 land
限定词:
MyProject/
res/
layout/ # default (portrait)
main.xml
layout-land/ # landscape
main.xml
layout-large/ # large (portrait)
main.xml
layout-large-land/ # large landscape
main.xml
Android 3.2 +
在Android 3.2 之前,其中一个困难是“庞大”的屏幕尺寸容器,其中囊括了 Dell Streak、原版 Galaxy Tab 以及常规 7 英寸平板电脑。 不过,尽管这些设备都被视为“大”屏设备,许多应用可能仍需要为此类别中的不同设备(如为 5 英寸和 7 英寸设备)显示不同的布局。 正因如此,Android 在 Android 3.2 中引入了“最小宽度”限定符。
最小宽度 限定符允许您将目标锁定在具有特定最小宽度(单位:dp)的屏幕。 例如,典型的 7 英寸平板电脑最小宽度为 600dp,因此,如果您希望您的 UI 在这些屏幕上显示两个窗格(但在较小屏幕上显示单个列表),您同样可以为单窗格布局和双窗格布局使用两种布局,但不使用 large 尺寸限定符,而是使用 sw600dp 为最小宽度是 600dp 的屏幕指定双窗格布局:
- res/layout/main.xml,单窗格(默认)布局:
<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>
- res/layout-sw600dp/main.xml,双窗格布局:
<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(单窗格)布局。
不过,这种方法在低于 3.2 版本的设备上不太奏效,因为它们无法将 sw600dp 识别为尺寸限定符,所以您仍需使用 large 限定符。因此,您应该建立一个与 res/layout-sw600dp/main.xml 完全相同的、名为 res/layout-large/main.xml 的文件。下文介绍的技巧可让您避免因此而产生重复的布局文件。
使用布局别名
如果您的应用的 minSdkVersion 大于等于 3.2,可以跳过此部分。
最小宽度限定符仅在 Android 3.2 及更高版本上提供。因此,您仍应使用兼容早期版本的抽象尺寸容器(小、正常、大和超大)。 例如,如果您想让自己设计的 UI 在手机上显示单窗格 UI,但在 7 英寸平板电脑、TV 及其他大屏设备上显示多窗格 UI,则需要提供下列文件:
- res/layout/main.xml: 单窗格布局
- res/layout-large: 多窗格布局
- res/layout-sw600dp: 多窗格布局
后两个文件虽然名字不同,但内容完全相同,因为其中一个将由 Android 3.2 + 设备匹配,另一个是为了照顾使用早期版本 Android 的平板电脑和 TV 的需要。
为避免为平板电脑和 TV 产生相同的重复文件(以及由此带来的维护难题),您可以使用别名文件。 例如,您可以定义下列布局:
- res/layout/main.xml,单窗格布局
- res/layout/main_twopanes.xml,双窗格布局
并添加以下两个文件:
- 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 的别名。由于这些文件具有 large 和 sw600dp 选择器,因此它们适用于任何 Android 版本的平板电脑和电视(低于 3.2 版本的平板电脑和电视匹配 large,高于 3.2 版本者将匹配 sw600dp)。
创建不同的位图
我们应该始终提供适当缩放到每个通用密度设备的位图资源:低,中,高和超高密度。这有助于App在所有屏幕密度上实现良好的图形质量和性能。
要生成这些图像,应该以矢量格式从原始资源开始,并使用以下尺寸比例为每个密度生成图片:
- xhdpi: 2.0
- hdpi: 1.5
- mdpi: 1.0 (baseline)
- ldpi: 0.75
例如:如果为xhdpi设备生成200x200图片,则应为hdpi生成150x150,mdpi为100x100,ldpi 为75x75的的相同资源。
然后,将文件放在适当的drawable资源目录中:
MyProject/
res/
drawable-xhdpi/
awesomeimage.png
drawable-hdpi/
awesomeimage.png
drawable-mdpi/
awesomeimage.png
drawable-ldpi/
awesomeimage.png
当在代码中引用@drawable/awesomeimage时,系统会根据当前设备的屏幕密度选择适当的位图。
低密度(ldpi)资源并不总是必需的。如果提供了hdpi资源时,系统将其缩小一半以适应ldpi屏幕。
使用九宫格位图
支持不同屏幕尺寸通常意味着您的图像资源也必须能够适应不同的尺寸。 例如,按钮背景必须能够适应其所应用到的任何一种按钮形状。
如果您在可能改变尺寸的组件上使用简单图像,您很快会发现效果有些差强人意,因为运行组件会均匀地拉伸或缩小您的图像。 解决方案是使用九宫格位图,这种特殊格式的 PNG 文件会指示哪些区域可以拉伸,哪些区域不可以拉伸。
因此,在设计将用于尺寸可变组件的位图时,请一律使用九宫格位图。