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資源,會自動根據當前屏幕分辨率進行選擇最佳資源。

主題三:

相關文章
相關標籤/搜索