0%

Android开发:屏幕适配-支持不同的屏幕尺寸

翻自Android官方文档:http://developer.android.com/training/multiscreen/screensizes.html 本节向你展示用以下方法支持不同的屏幕尺寸: 1、确保你的布局可以调整大小以适应不同屏幕 2、根据屏幕配置提供适当的UI布局 3、确保正确的布局应用到正确的屏幕 4、提供正确缩放的图片(不同的屏幕,图片大小不同) 使用wrap_content和match_parent 为了确保你的布局灵活并且适配不同的屏幕,你应该在宽度和高度上使用wrap_content和match_parent,使用wrap_content时,view会给宽高设置一个适应内容的最小值,当使用match_parent(api level 8前是fill_parent)组件会展开匹配父view的大小。 通过使用wrap_content和match_parent来替代硬编码的size(size写死),你的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而非指定大小。这允许布局对不同的屏幕大小和方向适配。 使用RelativeLayout 你可以用嵌套LinearLayout,wrap_content,match_parent构建相当复杂的布局。然而LinearLayout不能精确控制子视图间的相对关系。LinearLayout的子视图只是简单的一个挨着一个。如果你需要子视图在其他视图变化时调整,而不是一条直线,更好的解决方案是使用RelativeLayout,它允许你指定两个控件间的相对关系。下面的实例是一个视图屏幕左对齐,一个右对齐

<?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屏幕上的效果 大屏幕上的效果: 尽管大小变了,但是空间关系保存在RelativeLayout.LayoutParams中 使用尺寸限定符 可扩展布局或者相对布局能做的只能前面说的那么多。这些布局通过在空间内部拉伸或者依靠其他控件再适配不同的屏幕,他们可能没有为不同的屏幕提供最好的用户体验。因此,你的应用不应该只提供弹性布局,也要提供几个针对不同的屏幕可选的布局。你可以通过配置界定符,它允许你在运行时自动根据当前的设备配置选择合适的资源(比如不同的屏幕尺寸设计不同的布局) 举个例子,很多应用在大屏幕上实现两栏布局模式(一栏是列表,一栏是内容)。平板和电视的屏幕足够大可以同时显示两栏,但手机的屏幕必须分开显示,所以,实现这些布局,你可以通过下面的文件。 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界定符的第二个布局,这个布局会被屏幕分类为larger的设备自动选择,比如屏幕大于7寸的平板。小屏设备会选择另一个视图(没有large界定符的) 使用最小宽度(Smallest-width)界定符 开发者们需要面对的难题之一是3.2以前的Android设备”large”屏幕尺寸,包括Dell Streak,Galaxy Tab和大部分7寸平板。然而,很多应用想要为不同的设备(都是Large)屏幕上显示出不出的效果(像5寸,7寸),虽然他们都是large.简单地说,large分得不够细. 最小宽度界定符允许你设置一个最小宽度(dp为单位)来适配屏幕。例如,典型的7寸平板是600dp,如果你想要你的UI在这种屏幕上有两栏,但是在更小的屏幕上显示一栏,你可以使用前一节中用到的两个layout(一栏,两栏),但两栏的目录不是用large界定符,而是sw600dp。 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的设备会选择res/layout-sw600dp/main.xml,而小于600dp的设备会选择res/layout/main.xml Android 3.2以前的版本支持上面的写法,你还是得用large,所以你的应用里,得有layout-large,还要有一个内容与layout-large相同的layout-sw600dp(抛弃这部分用户吧)。 为了减少维护成本,避免复制多个一样的文件,你可以使用别名定义。例子: res/layout/main.xml, 一栏布局 res/layout/main_twopanes.xml, 两栏布局 res/values-large/layout.xml,res/values-sw600dp/layout.xml里引用:

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

使用方向界定符 一些布局在横竖屏方向都能工作得很好,但大多数还是可以通过调整受益。下面是在新闻阅读器的例子中,不同屏幕尺寸和不同方向的不同表现: 小屏竖屏 单栏,带logo 小屏横屏 单栏,带logo 7寸平板 竖屏 单栏,带action bar 7寸平板 横屏 双栏,宽,带action bar 10寸平板 竖屏 双栏,窄,带action bar 10寸平板 横屏 双栏 宽 带action bar 电视 横屏 双栏 宽 带action bar 每个布局都定义在res/layout目录中,然后针对不同的屏幕配置使用别名。 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>

使用nine-patches图片(后缀为.9.png) 支持不同的屏幕尺寸通常意味着你的图片资源也能够适配不同的尺寸,举例来说,一个按钮的背景必须适应按钮可能的宽高变化 如果你在尺寸可以改变的组件上使用简单的图片,你很快会意识到不是那么好看,因为图片在运行时会被拉伸变形。解决方案是使用nine-patches图片.这是一种能够判断哪些区域可以或者不可以拉伸的png格式图片。 因此,当要为可变尺寸的控件设计图片时,总是使用nine-patches图片,你可以把普通的图片转换成nine-patches。 制作Nine-patch图片通常使用sdk/tools/目录下的draw9patch工具,可以通过在左边和顶部边框上画像素来标记哪个区域可以被拉伸,你也可以在右边和底部边框画像素标记哪块区域可以显示内容,结果如下图: 注意边框上的黑色像素,左边和顶部的用于指明图片的哪个部位可以被拉伸,右边和底部的用于指明内容应该放哪。同时,注意.9.png的扩展名,Android会根据这个识别nine-patch图片,可别把9去掉了. 当你把这个图片应用到组件上时(通过设置android:background=”@drawable/button”),Android框架会自动拉伸图片适应按钮的尺寸,看下图: