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

原文地址: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的扩展名。必须使用该扩展名,因为这是框架将其与普通图片区分的一种方式。

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

时间: 2024-12-14 08:35:56

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

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

原文地址 : http://android.xsoftlab.net/training/index.html 引言 在翻译了一篇安卓的官方文档之后,我觉得应该做一件事情,就是把安卓的整篇训练课程全部翻译成英文,供国内的开发者使用,尤其是入门开发者,虽然现在网络上有很多入门课程,但是还是依靠官方文档学习来的靠谱,安卓官方文档是一系列的课程,使每个人可以系统的掌握安卓的知识,相比其它课程来说,它为开发者提供了查缺补漏的功能. 在这里你可以领略到安卓开发世界的精彩. Tips : 同时,本目录可以作为

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

原文地址:http://android.xsoftlab.net/training/graphics/opengl/draw.html 如果你还不清楚如何定义图形及坐标系统,请移步:Android官方开发文档Training系列课程中文版:OpenGL绘图之图形定义. 在定义了图形之后,你接下来需要做的就是将它绘制到屏幕上.不过使用OpenGL ES 2.0 API来绘制这个图形所需要的代码量可能要比想象中的多一些,这是因为API为图形渲染管道提供了大量的控制细节. 这节课会展示如何绘制上节课所

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

原文地址:http://android.xsoftlab.net/training/gestures/scroll.html 在Android中,滑动经常由ScrollView类来实现.任何超出容器边界的布局都应该将自己内嵌在ScrollView中,以便提供可滚动的视图效果.自定义滚动只有在特定的场景下才会被用到.这节课将会描述这样一种场景:使用scroller显示一种可滚动的效果. 你可以使用Scroller或者OverScroller来收集一些滑动动画所需要的数据.这两个类很相似,但是Ove

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

原文地址:http://android.xsoftlab.net/training/system-ui/index.html 引言 系统条(System Bars)是屏幕上的一块显示区域,专门用来显示通知,设备的通讯状态以及设备的导向.典型的System Bars与APP同时显示在屏幕上.APP展示了具体的内容,比如电影或者照片,这时可以临时性的将System Bars的图标变暗,以便减少不必要的干扰,或者临时性的隐藏System Bars,以便进入一种身临其境的状态. 如果你对Android

Android官方开发文档Training系列课程中文版:通知用户之构建通知

原文地址:http://android.xsoftlab.net/training/notify-user/index.html 引言 通知用于在有事件发生时,将事情以更便捷的方式展示给用户.用户可以在他们方便的时候直接与通知交互. Notifications design guide课程讲述了如何设计有效的通知以及何时去使用它们.这节课将会学习如何实现通用的通知设计. 构建通知 这节课的实现主要基于NotificationCompat.Builder类,NotificationCompat.B

Android官方开发文档Training系列课程中文版:手势处理之多点触控处理

原文地址:http://android.xsoftlab.net/training/gestures/multi.html 多点触控是指多个手指同时触摸屏幕的情况.这节课主要学习如何检测多点触控手势. 记录多个触控点 当多根手指同时触碰到屏幕时,系统会产生以下触摸事件: ACTION_DOWN -第一个触碰到屏幕的点.它是手势的起始事件.这个触控点的指针数据在MotionEvent对象中的索引总是0. ACTION_POINTER_DOWN -除第一个触控点之外的其它点.这个触控点的指针数据的索

Android官方开发文档Training系列课程中文版:网络操作之网络连接

原文地址:http://android.xsoftlab.net/training/basics/network-ops/index.html 引言 这节课将会学习最基本的网络连接,监视网络连接状况及网络控制等内容.除此之外还会附带描述如何解析.使用XML数据. 这节课所包含的示例代码演示了最基本的网络操作过程.开发者可以将这部分的代码作为应用程序最基本的网络操作代码. 通过这节课的学习,将会学到最基本的网络下载及数据解析的相关知识. Note: 可以查看课程Transmitting Netwo

Android官方开发文档Training系列课程中文版:网络操作之XML解析

原文地址:http://android.xsoftlab.net/training/basics/network-ops/xml.html 扩展标记语言(XML)是一系列有序编码的文档.它是一种很受欢迎的互联网数据传输格式.像需要频繁更新内容的网站来说,比如新闻站点或者博客,需要经常更新它们的XML源,以使外部程序可以保持内容的同步变化.对于含有网络连接态的APP应用来说,上传及解析XML数据是一个通用的任务.这节课将会学习如何解析XML文档及如何使用XML中的数据. 选择解析器 我们推荐使用X

Android官方开发文档Training系列课程中文版:动画视图之转场框架介绍

原文地址:http://android.xsoftlab.net/training/transitions/index.html 引言 Activity所呈现的UI经常会由用户的输入或者其它事件而发生变化.比如,一个含有输入框的Activity,在用户输入要查找的关键字之后,这个输入框就会隐藏,并会在输入框的地方展示搜索后的结果. 为了可以在这样的情况下呈现出连贯的视觉效果,可以在不同View展示与隐藏过程中使用动画.这些动画可以为用户提供一种反馈,并会帮助他们学习应用是如何流转的. Andro