Android屏幕適配

屏幕適配

基礎知識

屏幕尺寸

屏幕尺寸指屏幕的對角線的長度,單位是英寸,1英寸=2.54釐米
常見的屏幕尺寸:3.5,3.7,4.2,5.0,5.15

屏幕分辨率

屏幕分辨率指在橫縱向上的像素點數,單位是px,1px = 1個像素點,一般以縱x橫 eg:1920 x 1080

屏幕像素密度

指每英寸上的像素點數單位是dpi,即“dot per inch”的縮寫。屏幕像素密度與屏幕尺寸和屏幕分辨率有關,在單一變化條件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小。

安卓中的單位
  • px
    像素點
  • dip 與 dp :
    Density Independent Pixels的縮寫,即密度無關像素,上面我們說過,dpi是屏幕像素密度,假如一英寸裏面有160個像素,這個屏幕的像素密度就是160dpi,那麼在這種情況下,dp和px如何換算呢?在Android中,規定以160dpi爲基準,1dip=1px,如果密度是320dpi,則1dip=2px,以此類推。
  • sp :
    scale-independent pixels,與dp類似,可以根據文字大小首選項進行縮放,若果文字設置成dp,則不會進行縮放
以dpi分類屏幕

mdpi、hdpi、xhdpi、xxhdpi用來修飾drawable文件夾以及values文件夾,用來區分不同像素密度下的圖片和dimen值。

名稱 像素密度範圍
mdpi 120dpi ~ 160dpi
hdpi 160dpi ~ 240dpi
xhdpi 240dpi ~ 320dpi
xxhdpi 320dpi ~ 480dpi
xxxhdpi 480dpi ~ 640dpi

在設計圖標時,對於mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi應該按照2:3:4:6:8比例進行縮放
圖標對應尺寸如下:

屏幕密度 圖標尺寸
mdpi 48x48px
hpi 72x72px
xhdpi 96x96px
xxhdpi 144x144px
xxxhdpi 192x192px
尺寸限定詞

尺寸限定詞主要指在編寫佈局文件時,將佈局文件放在加上蕾絲large,sw600dp等這樣的限定詞文件夾中,以此來告訴屏幕選擇對應的佈局文件。

標識符 意思
sw< N >dp 最短的可用的屏幕區域(smallestWidth)
w < N >dp 最小的可用屏幕寬度(Available width)
h < N >dp 最小的可用屏幕高度(Available Height)
port 在縱向的情況下選擇
land 橫向的情況下選擇

可以使用‘-’來指定多個分離。
EG:
在佈局文件中加入sw600dp佈局,「layout-sw600dp」表示寬度在600dp或以上的設備
「layout-h1024dp-port」 表示高度大於或等於1024dp,縱向顯示的情況

更多限定相關內容請點擊下面鏈接

更多限定符內容

屏幕適配

使用“wrap_content” 和 “match_parent”

通過使用“wrap_content”和“match_parent”代替硬編碼的尺寸,EG:

<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>
使用LinearLayout的weight屬性做對應比重分配

weight是Linearlayout特有的屬性,可以通過給LinearLayout的子控件設置weight值,來分配比重

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="100dp">
        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:background="@color/colorAccent"
            android:layout_weight="1"
            />
        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:background="@color/colorPrimary"
            android:layout_weight="1"
            />
</LinearLayout>
使用相對佈局(RelativeLayout)

使用Relative可以明確指定子View的位置

<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>
使用尺寸限定詞

開發時,同時爲大屏幕實現“兩個窗格模式”,EG:平板和電視可能在一個屏幕上適應兩個窗格

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>
使用最小寬度限定詞

在安卓3.2開始使用最小寬度限定詞,最小寬度限定詞允許你根據設備的最小寬度(dp單位)來指定不同佈局。

EG:傳統的7寸平板最小寬度爲600dp,如果你希望你的UI能夠在這樣的屏幕上顯示兩個窗格(不是一個窗格顯示在小屏幕上),你可以使用上節中提到的使用同樣的兩個佈局文件。不同的是,使用sw600來指定兩個方框的佈局使用在最小寬度爲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>
使用佈局別名

最小寬度限定詞只能在android3.2或者更高的版本上使用。因此,你還是需要使用抽象尺寸(small,normal,large,xlarge)來兼容以前的版本。比如,你想要將你的UI設計爲在手機上只顯示一個方框的佈局,而在7寸平板或電視,或者其他大屏幕設備上顯示多個方框的佈局,你可能得提供這些文件:

  • res/layout/main.xml:單個窗格佈局
  • res/layout-large:多個窗格佈局
  • res/layout-sw600dp:多個窗格佈局

最後兩個文件都是一樣的,因爲其中一個將會適配Android3.2的設備,而另外一個則會適配其他Android低版本的平板或者電視。 爲了避免這些重複的文件(維護讓人感覺頭痛就是因爲這個),你可以使用別名文件。比如,你可以定義如下佈局:

  • res/layout/main.xml,單個方框佈局
  • res/layout/main_twopans.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選擇器中,所以它們能適配Android任何版本的平板和電視(在3.2之前平板和電視可以直接匹配large,而3.2或者以上的則匹配sw600dp)。

使用方向限定詞

有一些佈局不管是在橫向還是縱向的屏幕配置中都能顯示的非常好,但是更多的時候,適當的調整一下會更好。在News Reader應用例子中,以下是佈局在不同屏幕尺寸和方向的行爲:

  • 小屏幕,縱向:一個窗格加logo
  • 小屏幕,橫向:一個窗格加logo
  • 7寸平板,縱向:一個窗格加action bar
  • 7寸平板,橫向:兩個寬窗格加action bar
  • 10寸平板,縱向:兩個窄窗格加action bar
  • 10寸平板,橫向:兩個寬窗格加action bar
  • 電視,橫向:兩個寬窗格加action bar

這些每個佈局都會在res/layout目錄下定義一個xml文件,如此,應用就能根據屏幕配置的變化根據別名匹配到對應的佈局來適應屏幕。

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.png圖片

支持不同的屏幕尺寸同時也意味着你的圖片資源也必須能兼容不同的屏幕尺寸。如果你在使用組件時可以改變圖片的大小,你很快就會發現這是一個不明確的選擇。因爲運行的時候,圖片會被拉伸或者壓縮(這樣容易造成圖片失真)。避免這種情況的解決方案就是使用點9圖片,這是一種能夠指定哪些區域能夠或者不能夠拉伸的特殊png文件。

使用密度獨立像素(dp)

設計佈局時,要避免使用絕對像素(absolutepixels)定義距離和尺寸。使用像素單位來定義佈局大小是有問題的。因爲,不同的屏幕有不同的像素密度,所以,同樣單位的像素在不同的設備上會有不同的物理尺寸。因此,在指定單位的時候,通常使用dp或者sp。一個dp代表一個密度獨立像素,也就相當於在160 dpi的一個像素的物理尺寸,sp也是一個基本的單位,不過它主要是用在文本尺寸上(它也是一種尺寸規格獨立的像素),所以,你在定義文本尺寸的時候應該使用這種規格單位(不要使用在布尺寸上)。

EG:當你定義兩個View之間的空間時,應該使用dp而不是px:

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

當指定文本尺寸時,始終應該使用sp:

<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(標準線)
ldpi:0.75

這也就意味着如果在xhdpi設備上你需要一個200x200的圖片,那麼你則需要一張150x150的圖片用於hdpi,100x100的用於mdpi以及75x75的用戶ldpi設備。

然後將這些圖片資源放到res/對應的目錄下面,系統會自動根據當前設備屏幕密度自動去選擇合適的資源進行加載:

MyProject/
  res/
    drawable-xhdpi/
        awesomeimage.png
    drawable-hdpi/
        awesomeimage.png
    drawable-mdpi/
        awesomeimage.png
    drawable-ldpi/
        awesomeimage.png

這樣放置圖片資源後,不論你什麼時候使用@drawable/awesomeimage,系統都會給予屏幕的dp來選擇合適的圖片。

發佈了39 篇原創文章 · 獲贊 31 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章