Android应用程序支持不一样屏幕(尺寸、密度)

how to build a user interface using Android layouts for all types of devices 使用Android布局设计的UI接口用于不一样的Android设备html

Android provides a flexible framework for UI design that allows your app to display different layouts for different devices(针对不一样的设备显示不一样的布局), create custom UI widgets(自定义UI组件), and even control aspects of the system UI outside your app's window(控制系统UI).android

主题一:支持不一样屏幕尺寸app

有如下几种方式用于支持多屏幕:ide

1. 确保设计的布局可以自适应不一样尺寸的屏幕;布局

2. 可以根据屏幕配置提供适合的UI布局;性能

3. 确保正确的布局应用到正确的屏幕尺寸上;flex

4. 提供正确的Bitmap缩略文件;ui

在布局文件中使用“wrap_content”和“match_parent”编码

To ensure that your layout is flexible and adapts to different screen sizes, you should use "wrap_content" and "match_parent" for the width and height of some view components.spa

由于“wrap_content”将根据View的内容提供最小的布局空间;而“match_parent”则根据父容器(parent view)提供相同的布局大小。

经过使用“wrap_content”和“match_parent”代替硬编码尺寸,将分别只使用控件所须要的空间或者被拓展以填充全部有效的空间。

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

上述.xml布局文件自适应了不一样屏幕方向,注意组件的尺寸是自动适应宽和高的。

使用RelativeLayout布局

你可使用LinearLayout以及wrap_content和match_parent组合来构建复杂的布局,可是LinearLayout却不容许精确地控制它子View的关系;子View在线性布局中只能简单地一个接一个地排成行或列。若是想让子View不是简简单单地排成行或列,更好的方法是使用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>

尽管系统屏幕的尺寸发生了改变,可是它的子View之间的空间关系仍是经过RelativeLayout.LayoutParams已经指定好了。

使用尺寸限定

此处的限定词主要是指在编写布局文件时,将布局文件存放在相似large、sw600dp等这样限定词的文件夹中,以此来告诉系统根据屏幕选择对应的布局文件。好比下面例子中的layout-large文件夹。

咱们知道如何编写灵活的布局或者相对布局,它们都能经过拉伸或者填充控件来适应不一样的屏幕,可是它们却不能为每一个不一样屏幕尺寸提供最好的用户体验。所以,你的应用不该该只是实现灵活的布局,同时也应该为不一样的屏幕配置提供几种不一样的布局方式。

You do so by using configuration qualifiers, which allows the runtime to automatically select the appropriate resource based on the current device’s configuration (such as a different layout design for different screen sizes). 能够经过配置限定(Configuration Qualifies)来作这件事情,它能在运行时根据你当前设备的配置(好比:不一样的屏幕尺寸设计了不一样的布局)来选择合适的布局资源。

好比,不少应用都为大屏幕实现了“两个窗格”模式(应用可能在一个窗格中实现一个list的item,另一个则实现list的content),平板和电视都是大到能在一个屏幕上适应两个窗格,可是手机屏幕却只能分别显示。

res/layout/main.xml for single-pane(default) layout

<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 for two-pane layout

<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”限定词,在大尺寸的设备屏幕时(好比7寸平板或者其余大屏幕的设备)就会选择该布局文件,而其余比较小的设备则会选择没有限定词的另外一个布局(也就是第一个布局文件)。

使用最小宽度限定

在Android 3.2以前,开发者还有一个困难,那就是Android设备的“large”屏幕尺寸,其中包括Dell Streak(设备名称),老版Galaxy Tab和通常的7寸平板,有不少的应用都想针对这些不一样的设备(好比5和7寸的设备)定义不一样的布局,可是这些设备都被定义为了large尺寸屏幕。也是由于这个,因此Android在3.2的时候开始使用最小宽度限定词。最小宽度限定词容许你根据设备的最小宽度(dp单位)来指定不一样布局。好比,传统的7寸平板最小宽度为600dp,若是你但愿你的UI可以在这样的屏幕上显示两个窗格(不是一个窗格显示在小屏幕上),你可使用上节中提到的使用一样的两个布局文件。不一样的是,使用sw600来指定两个方框的布局使用在最小宽度为600dp的设备上。

将上述大屏幕文件名修改成:res/layout-sw600dp/main.xml

这样意味着当你的设备的最小宽度等于600dp或者更大时,系统选择layout-sw600dp/main.xml(两个窗格)的布局,而小一点的屏幕则会选择layout/main.xml(单个窗格)的布局。 然而,在3.2以前的设备上,这样作并非很好的选择。由于3.2以前尚未将sw600dp做为一个限定词出现,因此,你仍是须要使用large限定词来作。所以,你仍是应该要有一个布局文件名为res/layout-large/main.xml,和res/layout-sw600dp/main.xml同样。

使用布局别名

The smallest-width qualifier is available only on Android 3.2 and above.

为匹配不一样尺寸屏幕,通常的作法以下:

res/layout/main.xml --> single-pane layout

res/layout-large --> multi-pane layout

res/layout-sw600dp --> multi-pane layout

为了便于文件的维护工做,可使用文件别名(至关于使用文件选择器)

好比先定义两个文件,以下:

res/layout/main.xml, single-pane layout

res/layout/main_twopanes.xml, two-pane layout

如上,是在两个相同的文件夹下的文件。

为了使用文件选择器,能够定义如下文件: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_twopanes设置成为了别名main,分别处在large和sw600dp选择器中,它们可以适配Android任何版本的平板和电视(在3.2以前平板和电视能够直接匹配large,而3.2或者以上的系统则匹配sw600dp)

系统会根据不一样系统属性选择不一样布局文件,若选择values-large资源容器,则选择layout/中的main_twopanes.xml;若选择values-sw600dp资源容器,则也会选择layout/中的main_twopanes.xml。但全部的布局文件都使用main.xml名字。

使用方向限定

在某个栗子中布局在不一样屏幕尺寸和方向的行为以下:

small screen, portrait   --> single pane, with logo
small screen, landscape  --> single pane, with logo
7" tablet, portrait      --> single pane, with action bar
7" tablet, landscape     --> dual pane, wide, with action bar
10" tablet, portrait     --> dual pane, narrow, with action bar
10" tablet, landscape    --> dual pane, wide, with action bar
TV, landscape            --> dual pane, wide, with action bar

根据上述行为,总结出了四种不一样的布局状况:

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</item>
    <bool name="has_two_panes">true</bool>
</resources>

此外,对于支持不一样尺寸屏幕可使用Nine-patch图片。

主题二:支持不一样屏幕密度

使用独立分辨率

对于不一样屏幕密度的设备,咱们可以作的是提供不一样的资源和使用独立分辨率以提供支持。

使用像素单位来定义布局大小是有问题的。由于不一样的屏幕有不一样的像素密度,一样单位的像素在不一样的设备上会有不一样的物理尺寸。“dp”表明一个密度独立像素,也就至关于在160dpi的一个像素的物理尺寸;“sp”也是一个基本的单位,不过它主要是用在文本尺寸上(它也是一种尺寸规格独立的像素),因此在定义文本尺寸的时候应该使用这种规格单位。

当咱们在定义两个View之间的距离时,使用下述实践:

<Button android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="@string/clickme"
    android:layout_marginTop="20dp" />

当咱们定义文本大小时,使用下述实践:

<TextView android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:textSize="20sp" />

提供可供选择的图片

由于Android能运行在不少不一样屏幕密度的设备上,因此,你应该针对不一样的设备密度提供不一样的bitmap资源:小屏幕(low),medium(中),high(高)以及超高(extra-high)密度。这将能帮助你在全部的屏幕密度中获得很是好的图形质量和性能。

为了提供更好的用户体验,你应该使用如下几种规格来缩放图片大小,为不一样的屏幕密度提供相应的位图资源:

xhdpi: 2.0
hdpi: 1.5
mdpi: 1.0 (baseline)
ldpi: 0.75 

This means that if you generate a 200x200 image for xhdpi devices, you should generate the same resource in 150x150 for hdpi, 100x100 for mdpi and finally a 75x75 image for ldpi devices.这也就意味着若是在xhdpi设备上你须要一个200x200的图片,那么你则须要一张150x150的图片用于hdpi,100x100的用于mdpi以及75x75的用户ldpi设备。而后将这些图片资源放到res/对应的目录下面,系统会自动根据当前设备屏幕密度自动去选择合适的资源进行加载。

以后,系统在运行时的任什么时候候,须要使用到@drawable/awesomeimage资源,会自动根据当前屏幕分辨率进行选择最佳资源。

主题三:

相关文章
相关标签/搜索