關鍵字:屏幕適配
px
dp
dpi
sp
large限定符
.9.png
前言:這篇文章依然是我在 [慕課網 ][h]學習 凱子哥 的同名視頻 Android-屏幕適配全攻略 ,所記錄下來的筆記---凱子哥講得真的超詳細。
[h]: http://www.imooc.com/ "MOOC"
2012年到2014年支持Android設備的種類從3997增長到18796。同時各大廠商定制的屏幕尺寸也非常多。這將非常不利于我們進行屏幕適配。這要求我們必須掌握屏幕適配技能,以便使我們的app可以適用于不同屏幕尺寸的設備上。
從上圖可以看出,主流的分辨率是前六種:1280×720、1920×1080、800×480、854×480、960×540、1184×720,不過我們有解決方案。看完這篇文章,想必你就可以解決常見的屏幕適配問題。
接下來正式進入正題。
一. 核心概念與單位詳解
介紹幾個在Android屏幕適配上非常重要的名詞:
1. 什么是屏幕尺寸、屏幕分辨率、屏幕像素密度?
屏幕尺寸是指屏幕對角線的長度。單位是英寸,1英寸=2.54厘米
屏幕分辨率是指在橫縱向上的像素點數,單位是px,1px=1像素點,一般是縱向像素橫向像素,如1280×720
屏幕像素密度是指每英寸上的像素點數,單位是dpi,即“dot per inch”的縮寫,像素密度和屏幕尺寸和屏幕分辨率有關
例如:計算Nexus5的屏幕像素密度:
屏幕尺寸:4.95inch、分辨率:1920×1080,屏幕像素密度:445
和官方給出的一樣,說明我們計算正確。
2. 什么是dp、dip、dpi、sp、px?之間的關系是什么?
dip:Density Independent Pixels(密度無關像素)的縮寫。以160dpi為基準,1dp=1px
dp:同dip
dpi:屏幕像素密度的單位,“dot per inch”的縮寫
px:像素,物理上的絕對單位
sp:Scale-Independent Pixels的縮寫,可以根據文字大小首選項自動進行縮放。Google推薦我們使用12sp以上的大小,通??梢允褂?2sp,14sp,18sp,22sp,最好不要使用奇數和小數。
說明:如果A設備的參數為480×320,160dpi,B設置的參數為800×480,240dpi。我們要畫出一條和屏幕寬度一樣長的直線,如果使用
px
作為單位,必須在A設備上設置為320px,在B設備上設置480px。但是如果我們使用dp
作為單位,由于以160dpi為基準,1dp=1px
,所以A設備上設置為320dp就等于屏幕寬度(320px),在B設備上設置為320dp就等于320×(240/160)=480px,即B設備的屏幕寬度。這樣,使用dp
作為單位就可以實現簡單的屏幕適配。這知識一種巧合,也有B設備的像素密度不是這樣剛剛好的,就需要我們運用別的屏幕適配技術。
3. 什么是mdpi、hdpi、xdpi、xxdpi、xxxdpi?如何計算和區分?
用于區分不同的像素密度。
名稱 | 像素密度范圍 | 圖片大小 |
---|---|---|
mdpi | 120dp~160dp | 48×48px |
hdpi | 160dp~240dp | 72×72px |
xhdpi | 240dp~320dp | 96×96px |
xxhdpi | 320dp~480dp | 144×144px |
xxxhdpi | 480dp~640dp | 192×192px |
在Google官方開發文檔中,說明了 ** mdpi:hdpi:xhdpi:xxhdpi:xxxhdpi=2:3:4:6:8 ** 的尺寸比例進行縮放。例如,一個圖標的大小為48×48dp,表示在mdpi上,實際大小為48×48px,在hdpi像素密度上,實際尺寸為mdpi上的1.5倍,即72×72px,以此類推。
二. 解決方案-支持各種屏幕尺寸
我們可以通過以下幾種方式來支持各種屏幕尺寸:
1. 使用wrap_content、math_parent、weight
wrap_content:根據控件的內容設置控件的尺寸
math_parent:根據父控件的尺寸大小設置控件的尺寸
weight:權重,在線性布局中可以使用weight屬性設置控件所占的比例
例如,我們要實現下圖所顯示的效果:當屏幕尺寸改變時,new reader控件兩邊的控件大小不變,new reader控件會占完剩余的空間。
實現:通過給new reader控件設置屬性:
android:layout_width="wrap_content"
,android:layout_weight="1"
,兩邊兩個控件為固定大小,android:layout_weight="0"
具體布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_weight="0"
android:background="#028330"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="80dp"
android:text="new
reader"
android:textSize="22sp"
android:layout_weight="1"/>
<TextView
android:layout_width="160dp"
android:layout_height="80dp"
android:text="Politics"
android:textSize="18sp"
android:layout_weight="0"
android:background="#028330"/>
</LinearLayout>
小插曲:關于android:layout_weight
屬性
公式:所占寬度=原來寬度+剩余空間所占百分比的寬度
一般情況,我們都是設置要進行比例分配的方向的寬度為0dp,然后再用權重進行分配。如下:
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Button1" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Button2" />
效果為:
設屏幕寬度為L,
根據公式,
button1寬度=0+L×1/(1+2)=1/3L
button2寬度=0+L×2/(1+2)=2/3L
但如果設置為
match_parent
:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Button1" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Button2" />
效果為:
button1寬度=L+(L-2L)×1/3=2/3L
button2寬度=L+(L-2L)×2/3=1/3L
當然,還有其他的方式,都可以運用此公式進行計算。
在實際開發中,我們一般使用0dp的方式,而不使用其他方式。
2. 使用相對布局,禁用絕對布局
簡單的布局一般都使用線性布局
,而略微復雜點的布局,我們使用相對布局
,大多數時候,我們都是使用這兩種布局的嵌套。
我們使用相對布局
的原因是,相對布局
能在各種尺寸的屏幕上保持控件間的相對位置。
3. 使用限定符
-
使用尺寸限定符
當我們要在大屏幕上顯示不同的布局,就要使用large
限定符。例如,在寬的屏幕左邊顯示列表右邊顯示列表項的詳細信息,在一般寬度的屏幕只顯示列表,不顯示列表項的詳細信息,我們就可以使用large
限定符。
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>
如果這個程序運行在屏幕尺寸大于7inch的設備上,系統就會加載res/layout-large/main.xml
而不是res/layout/main.xml
,在小于7inch的設備上就會加載res/layout/main.xml
。
需要注意的是,這種通過large
限定符分辨屏幕尺寸的方法,適用于android3.2之前。在android3.2之后,為了更精確地分辨屏幕尺寸大小,Google推出了最小寬度限定符。
-
使用最小寬度限定符
最小寬度限定符的使用和large
基本一致,只是使用了具體的寬度限定。
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
,雙面板布局: Small Width 最小寬度
<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之后,所以如果要適配android全部的版本,就要使用large
限定符和sw600dp
文件同時存在于項目res
目錄下。
這就要求我們維護兩個相同功能的文件。為了避免繁瑣操作,我們就要使用布局別名。
-
使用布局別名
res/layout/main.xml
: 單面板布局
res/layout-large/main.xml
: 多面板布局
res/layout-sw600dp/main.xml
: 多面板布局
由于后兩個文具文件一樣,我們可以用以下兩個文件代替上面三個布局文件:
res/layout/main.xml
單面板布局
res/layout/main_twopanes.xml
雙面板布局
然后在res
下建立
res/values/layout.xml
、
res/values-large/layout.xml
、
res/values-sw600dp/layout.xml
三個文件。
默認布局
res/values/layout.xml
:
<resources>
<item name="main" type="layout">@layout/main</item>
</resources>
Android3.2之前的平板布局
res/values-large/layout.xml
:
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
Android3.2之后的平板布局
res/values-sw600dp/layout.xml
:
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
這樣就有了main
為別名的布局。
在activity中setContentView(R.layout.main);
這樣,程序在運行時,就會檢測手機的屏幕大小,如果是平板設備就會加載res/layout/main_twopanes.xml
,如果是手機設備,就會加載res/layout/main.xml
。我們就解決了只使用一個布局文件來適配android3.2前后的所有平板設備。
- 使用屏幕方向限定符
如果我們要求給橫屏、豎屏顯示的布局不一樣。就可以使用屏幕方向限定符
來實現。
例如,要在平板上實現橫豎屏顯示不用的布局,可以用以下方式實現。
res/values-sw600dp-land/layouts.xml
:橫屏
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
res/values-sw600dp-port/layouts.xml
:豎屏
<resources>
<item name="main" type="layout">@layout/main</item>
</resources>
4. 使用自動拉伸位圖
自動拉伸位圖,即android下特有的.9.png
圖片格式。
當我們需要使圖片在拉伸后還能保持一定的顯示效果,比如,不能使圖片中的重要像素拉伸,不能使內容區域受到拉伸的影響,我們就可以使用.9.png
圖來實現。
要使用.9.png
,必須先得創建.9.png
圖片,androidSDK給我們提供了的工具就包含.9.png
文件的創建和修改工具。雙擊SDK安裝目錄 oolsdraw9patch.bat
,就會打開下圖所示的窗口。
直接把圖片拖進去,選擇Filesave.9.png,即可保存為
.9.png
圖片。
不過,這和原圖沒有什么區別,我們要制作成自動拉伸的圖片,還需要進行簡單的處理。在左邊和上邊點下一個像素或多個像素點,是選擇了拉伸的像素,即把選擇的像素點進行拉伸。選擇的像素點越多,拉伸的程度也越大。
右邊和下邊是選擇內容區域,在右邊和下邊畫上直線,交叉的區域就是內容區域。
什么事內容區域呢?
比如,我們給Button設置了一個.9.png
作為背景,還要設置其android:text
屬性,設置的text所占的位置就是內容區域。我們必須保證內容區域包含text文本,才會正常顯示出text文本的內容。
下面是一個例子:
上圖是我們制作的
.9.png
,設置好了左上拉伸像素點和內容區域。
Button屬性設置:
<Button
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@drawable/button"
android:text="Button" />
如果我們選擇的內容區域偏差太大,可能就不會顯示出text值BUTTON
。
好了,這篇文章寫的有點多了,剩下的內容放在 下篇文章 記錄吧。
內容提要:
解決方案-支持各種屏幕密度
解決方案-實施自適應用戶界面流程
未完待續