程序员人生 网站导航

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

栏目:综合技术时间:2016-08-03 08:52:04

原文地址: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间的相对关系,1种较好的解决方式就是使用RelativeLayout,它允许指定View间的相对关系。下面的示例中,你可以指定1个View靠着另外一个View的左侧,而另外一个View的右侧则靠着屏幕的右侧。

<?xml version="1.0" encoding="utf⑻"?> <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”的模式。平板与电视大到足以同时显示两个面板,但是移动电话只能同时显示其中1个。所以,要实现这类布局,项目中应当含有以下文件:

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

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

使用最小宽度标识符

开发者会遇到的困难之1就是在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标识符,所以会有1个名为res/layout-large/main.xml的布局文件,其中的内容与res/layout-sw600dp/main.xml保持1致。下面的部份将会介绍1种技术来避免重复的布局文件。

使用布局别称

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

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

这后面两个文件是完全相同的,由于其中1个是用来匹配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而已。1旦这些文件包括了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>

使用9宫格位图

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

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

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

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

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

最后还要注意.9.png的扩大名。必须使用该扩大名,由于这是框架将其与普通图片辨别的1种方式。

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

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐