原文地址:http://android.xsoftlab.net/training/multiscreen/index.html

引言

Android运行于数以百计不同尺寸的设备上。范围小到手持移动电话,大到电视设备。因此,在设计APP时应当兼顾到尽可能多的屏幕尺寸。这样才能照顾到较多的潜在用户。

但是仅仅考虑不同的设备类型还不够。每一种尺寸为用户提供了不同的可能性与挑战,所以为了使用户感到满意,应用程序需要做的不单单是支持多样的屏幕:它还必须对每种屏幕结构将用户体验优化到最佳。

这节课将会学习如何实现针对屏幕结构优化的用户界面。

Note: 这节课与相关示例程序均使用的是support library。

支持不同的屏幕尺寸

这节课将会学习通过以下方式来支持不同的屏幕尺寸:

  • 确保布局可以灵活的调整尺寸。
  • 对不同的屏幕结构提供适当的UI布局。
  • 确保在正确的屏幕中使用了正确的布局。
  • 提供可以正常缩放的位图。

使用”wrap_content”及”match_parent”

为了使布局可以灵活的适配不同的屏幕尺寸,应当对某些View组件的width,height属性使用”wrap_content”或”match_parent”。如果使用了”wrap_content”,那么View的高宽会被设置为View内容所需的最小尺寸。然而”match_parent”会使View的高宽扩展到父布局的尺寸大小。

通过使用”wrap_content”或”match_parent”可以使View高宽扩展到View所需要的大小或者扩展到父布局的可用空间:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayout android:layout_width="match_parent" android:id="@+id/linearLayout1"  android:gravity="center"android:layout_height="50dp"><ImageView android:id="@+id/imageView1" android:layout_height="wrap_content"android:layout_width="wrap_content"android:src="@drawable/logo"android:paddingRight="30dp"android:layout_gravity="left"android:layout_weight="0" /><View android:layout_height="wrap_content" android:id="@+id/view1"android:layout_width="wrap_content"android:layout_weight="1" /><Button android:id="@+id/categorybutton"android:background="@drawable/button_bg"android:layout_height="match_parent"android:layout_weight="0"android:layout_width="120dp"style="@style/CategoryButtonStyle"/></LinearLayout><fragment android:id="@+id/headlines" android:layout_height="fill_parent"android:name="com.example.android.newsreader.HeadlinesFragment"android:layout_width="match_parent" />
</LinearLayout>

注意示例中是如何使用”wrap_content”及”match_parent”的。这可以使布局正确的适配不同的屏幕尺寸及方向。

下图是布局在垂直及水平方向的示例。注意View的尺寸会自动适配屏幕的高宽:

使用RelativeLayout

你可以使用LinearLayout结合”wrap_content”或”match_parent”构造相对复杂的布局。然而,LinearLayout不能够精确的控制子View的相对关系。在LinearLayout中View只能简单的被线性排列。如果需要调整View间的相对关系,一种较好的解决方式就是使用RelativeLayout,它允许指定View间的相对关系。下面的示例中,你可以指定一个View靠着另一个View的左边,而另一个View的右边则靠着屏幕的右边。

<?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屏幕中的显示效果:

下图是该布局在大屏幕中的显示效果:

要注意虽然这些View的尺寸发生了改变,但是其它之间的相对关系还是保留了下来。

使用尺寸限定符

上面我们学习了如何利用灵活布局或者相对布局来匹配不同的屏幕,然而这对于匹配任何屏幕来说还不够好。因此,应用程序不单单只是实现灵活的布局,还应该对不同的屏幕配置提供相应的布局。可以通过configuration qualifiers中所描述的内容学习具体细节,它可以使程序在运行时根据当前的屏幕配置来自动选择对应的资源。

比如说,很多应用程序针对于大屏幕实现了”two pane”的模式。平板与电视大到足以同时显示两个面板,但是移动电话只能同时显示其中一个。所以,要实现这种布局,项目中应当含有以下文件:

  • 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-large/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>

要注意第二个布局的目录路径的large标识符。这个布局会在屏幕类型为large时被采用(比如,7英寸的平板或者更大的设备)。其它布局则会被小型设备所采用。

使用最小宽度标识符

开发者会遇到的困难之一就是在3.2之前Android的设备只有”large”屏幕尺寸,这包括了Dell Streak、Galaxy Tab以及常规的7英寸平板。然而,很多应用程序希望可以在这个范围下不同尺寸的设备中展示不同的布局,比如5英寸的设备或者7英寸的设备,甚至是所有的”large”设备都想考虑在内。这就是为什么Android会3.2的版本中引入”最小宽度(Smallest-width)”标识符的原因。

最小宽度限定符允许将最小宽度为给定的dp宽度的设备作为目标。比如说,经典的7英寸平板的最小宽度为600dp,如果希望可以在这块屏幕上同时放置两个面板的话,可以直接使用上面部分中所介绍的双面板布局。不过这里则不是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之前并不会有什么效果,因为3.2之前的系统识别不出sw600dp这种尺寸标识符,所以最好还是保留large标识符,所以会有一个名为res/layout-large/main.xml的布局文件,其中的内容与res/layout-sw600dp/main.xml保持一致。下面的部分将会介绍一种技术来避免重复的布局文件。

使用布局别称

最小宽度限定符只在Android 3.2上开始可用。因此,开发者还应当继续使用抽象尺寸标志(small, normal, large及xlarge)来兼容较早的版本。所以,如果希望在移动电话中显示单面板UI,在其它较大的屏幕中采用多面板UI,那么项目中应该含有以下文件:

  • res/layout/main.xml:单面板布局
  • res/layout-large:多面板布局
  • res/layout-sw600dp:多面板布局

这后面两个文件是完全相同的,因为其中一个是用来匹配Android 3.2的设备的,而另一个是用来匹配较早版本的设备的。

为了避免存在这种重复的文件,可以使用别名文件技术。比如,你可以定义如下布局文件:

  • 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_twopanes的别名设置为了main而已。一旦这些文件包含了large 或sw600dp,那么所有的系统则不会再专门区分版本。

使用方向标识符

有些布局在垂直及水平方向上均表现良好。但在新闻阅读示例APP中,针对每一种屏幕尺寸与方向均专门定义了布局:

  • small screen, portrait: 单面板,带Logo
  • small screen, landscape : 单面板,带Logo
  • 7” tablet, portrait : 单面板,带ActionBar
  • 7” tablet, landscape : 多面板,带ActionBar
  • 10” tablet, portrait : 多窄面板,带ActionBar
  • 10” tablet, landscape : 多面板,带ActionBar
  • TV, landscape : 多面板,带ActionBar

上面所有的布局文件都被放置在res/layout/目录下。为了使每一种布局与相关的屏幕配置产生关联,App使用布局别名的方式来匹配每一项配置:

res/layout/onepane.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/onepane_with_bar.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayout android:layout_width="match_parent" android:id="@+id/linearLayout1"  android:gravity="center"android:layout_height="50dp"><ImageView android:id="@+id/imageView1" android:layout_height="wrap_content"android:layout_width="wrap_content"android:src="@drawable/logo"android:paddingRight="30dp"android:layout_gravity="left"android:layout_weight="0" /><View android:layout_height="wrap_content" android:id="@+id/view1"android:layout_width="wrap_content"android:layout_weight="1" /><Button android:id="@+id/categorybutton"android:background="@drawable/button_bg"android:layout_height="match_parent"android:layout_weight="0"android:layout_width="120dp"style="@style/CategoryButtonStyle"/></LinearLayout><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/twopanes.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>

res/layout/twopanes_narrow.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="200dp"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>

以上对所有可能的布局均作了定义,它们会与相关的屏幕配置产生映射关系:
res/values/layouts.xml:

<resources><item name="main_layout" type="layout">@layout/onepane_with_bar</item><bool name="has_two_panes">false</bool>
</resources>

res/values-sw600dp-land/layouts.xml:

<resources><item name="main_layout" type="layout">@layout/twopanes</item><bool name="has_two_panes">true</bool>
</resources>

res/values-sw600dp-port/layouts.xml:

<resources><item name="main_layout" type="layout">@layout/onepane</item><bool name="has_two_panes">false</bool>
</resources>

res/values-large-land/layouts.xml:

<resources><item name="main_layout" type="layout">@layout/twopanes</item><bool name="has_two_panes">true</bool>
</resources>

res/values-large-port/layouts.xml:

<resources><item name="main_layout" type="layout">@layout/twopanes_narrow</item><bool name="has_two_panes">true</bool>
</resources>

使用九宫格位图

支持不同的屏幕尺寸同样意味着图片资源同样也需要自动适配不同的尺寸。比如,一张按钮的背景图必须匹配按钮的形状。

如果要将一张简图片应用在组件中,必须敏锐的意识到结果可能不是想象中那样,因为在运行时将会拉伸或者压缩图片。解决办法就是使用九宫格位图,它是一种特殊的PNG格式的文件,它内部指明了哪部分区域可以被拉伸,哪部分不可以。

因此,在设计位图时应当首先选用九宫格。为了将位图转化为九宫格位图,你可以从一张有规律的图片开始(下图被放大了4倍)。

然后通过draw9patch工具将该图片打开,该工具位于tools/目录下,它可以用来标记哪块区域可以被拉伸。拉伸标记位于图片的左边和顶部。你也可以通过在右边及底部绘点的方式来定义内容区域,如下图所示:

注意边上那些黑色的像素点。左边和顶部的点指明了图像可以被拉伸的区域,右边和顶部的点指明了内容区域。

最后还要注意.9.png的扩展名。必须使用该扩展名,因为这是框架将其与普通图片区分的一种方式。

当在使用这张图片作为背景时,框架会将图片拉伸以适应按钮的尺寸,如下图所示:

Android官方开发文档Training系列课程中文版:多样屏幕之支持不同的屏幕尺寸相关推荐

  1. Android官方开发文档Training系列课程中文版:目录

    原文地址 : http://android.xsoftlab.net/training/index.html 引言 在翻译了一篇安卓的官方文档之后,我觉得应该做一件事情,就是把安卓的整篇训练课程全部翻 ...

  2. Android官方开发文档Training系列课程中文版:创建自定义View之View的创建

    原文地址:http://android.xsoftlab.net/training/custom-views/index.html 引言 Android框架含有大量的View类,这些类用来显示各式各样 ...

  3. Android官方开发文档Training系列课程中文版:OpenGL绘图之图形绘制

    原文地址:http://android.xsoftlab.net/training/graphics/opengl/draw.html 如果你还不清楚如何定义图形及坐标系统,请移步:Android官方 ...

  4. Android官方开发文档Training系列课程中文版:使用Fragment构建动态UI之Fragment创建

    原文地址:http://android.xsoftlab.net/training/basics/fragments/index.html 导言 为了在Android中创建动态的多面板用户界面,你需要 ...

  5. Android官方开发文档Training系列课程中文版:构建第一款安卓应用之入门指南

    入门指南 欢迎来到安卓开发训练课,在这里你可以找到一系列课程来描述如何使用现有的代码示例来重新适用到你的APP上,你可以在左侧的导航栏顶部看到在若干个大项里有若干个有组织的子项课程.(导航栏请参见官方 ...

  6. Android官方开发文档Training系列课程中文版:手势处理之滚动动画及Scroller

    原文地址:http://android.xsoftlab.net/training/gestures/scroll.html 在Android中,滑动经常由ScrollView类来实现.任何超出容器边 ...

  7. Android官方开发文档Training系列课程中文版:管理系统UI之变暗系统条

    原文地址:http://android.xsoftlab.net/training/system-ui/index.html 引言 系统条(System Bars)是屏幕上的一块显示区域,专门用来显示 ...

  8. Android官方开发文档Training系列课程中文版:创建自定义View之View的绘制

    原文地址:http://android.xsoftlab.net/training/custom-views/custom-drawing.html#draw 自定义View最重要的部分就是它的样子了 ...

  9. Android官方开发文档Training系列课程中文版:多样屏幕之支持不同的屏幕密度

    原文地址:http://android.xsoftlab.net/training/multiscreen/screendensities.html 这节课将会学习如何通过不同的资源以及独立的测量单位 ...

最新文章

  1. mysql 电商项目(一)
  2. php每个月头一天与最后一天,PHP获取每月第一天与最后一天
  3. 前端学习(632):转义字符
  4. mybatis-plus删除操作(逻辑与物理删除)
  5. Android Flutter实践内存初探
  6. 查看计算机数字证书,数字证书认不到怎么办?
  7. 经济学中VCG机制介绍与机制设计(mechanism design)
  8. 欧拉筛素数的应用-漂亮数
  9. 基于java的奖学金_基于Java的奖学金评定系统设计与实现毕业设计论文.doc
  10. uniapp navigateTo跳转失效
  11. git 出现错误fatal: Unable to create ‘project_path/.git/index.lock‘: File exists.
  12. C语言wifi程序代码,STM32F103 WIFI程序 C语言.docx
  13. 英语语法基础06(长难句训练)
  14. 【Captain America Sentinel of Liberty HD】美国队长:自由哨兵 v1.0.2
  15. 在移动硬盘中,安装CentOS 7双系统
  16. AutoJs学习-读取手机短信
  17. RENESAS ISL15100IRZ-T7 单端口差分线路驱动器
  18. jstl json数据 ajax,JSTL,JQuery,Ajax,Json
  19. python修改app定位_appnium定位+操作方式(python)
  20. Microsoft Office OneNote 2007十大优势

热门文章

  1. vector深拷贝与浅拷贝使用总结
  2. 程序员面试的一些注意点
  3. 在家过年这两天|多图
  4. 数据结构和算法,也就那么回事儿
  5. pthread 的坑
  6. VMware 安装kali——linux
  7. 一、MySQL查询学习笔记(基础查询、条件查询、排序查询、常见函数、分组查询 详解)
  8. LeetCode 1521. 找到最接近目标值的函数值(位运算)
  9. LeetCode 1976. 到达目的地的方案数(迪杰斯特拉 Python 优先队列)
  10. HDFS依然是存储的王者