Android 裝置不僅有不同的螢幕大小,包括手機、平板電腦、電視等,也都有不同像素大小的螢幕。一部裝置每英寸可能為 160 像素,另一部裝置在相同空間能容納 480 像素。如果您未考慮像素密度的這些變化,系統可能會縮放圖片,導致圖片模糊不清,或者圖片顯示的大小可能不正確。
本頁將說明如何使用各自解析度獨立的測量單位,並針對每個像素密度提供替代的點陣圖資源,藉此設計應用程式來支援不同的像素密度。
請觀看以下影片,一覽這些技巧。
如要進一步瞭解如何設計圖示素材資源,請參閱 Material Design 圖示規範。
使用密度獨立像素
請避免使用像素來定義距離或大小,不同螢幕的像素密度不同,因此相同數量的像素會對應不同裝置上的不同實體尺寸,因此使用像素定義維度是問題並不容易。
![圖片顯示兩部裝置範例,螢幕密度不同](https://developer.android.com/static/images/screens_support/densities-phone_2x.png?authuser=9&hl=zh-tw)
為了在不同密度的螢幕上保留 UI 的可見大小,請將 UI 設計為密度獨立像素 (dp) 做為測量單位。一個 dp 是大小在中密度螢幕 (160 dpi,或「baseline」密度) 上約一個像素的虛擬像素單位。Android 會將這個值轉譯為每一個密度對應的實際像素數量。
請看圖 1 中的兩部裝置。寬度為 100 像素的檢視畫面,在裝置左側顯示的比例會大上許多。凡是定義為 100 dp 的檢視畫面,在兩個螢幕上都會顯示相同大小。
定義文字大小時,您可以改用可縮放像素 (sp) 做為單位。根據預設,sp 單位的大小與 dp 相同,但會根據使用者偏好的文字大小調整大小。請勿在版面配置大小時使用 sp。
舉例來說,如要指定兩個檢視畫面之間的間距,請使用 dp:
<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" />
將 dp 單位轉換為像素單位
在某些情況下,您需要以 dp 表示尺寸,然後將其轉換為像素。dp 單位與螢幕像素的轉換如下:
px = dp * (dpi / 160)
注意:請不要將這個方程式硬式編碼來計算像素。請改用 TypedValue.applyDimension()
,這項功能會將多種類型的尺寸 (dp、sp 等) 轉換為像素。
假設有一個應用程式在使用者手指移動至少 16 像素時,辨識出捲動或快速滑過手勢。在基準螢幕上,使用者的手指必須移動 16 pixels
/ 160 dpi
(等於 1/10 英寸 (或 2.5 公釐) 的位置),系統才能辨識手勢。
在高密度螢幕 (240 dpi) 的裝置上,使用者的手指必須移動 16 pixels / 240 dpi
,這等於 1/15 英寸 (或 1.7 公釐)。距離越短,應用程式對使用者的靈敏度也就越高。
如要修正這個問題,請以 dp 為單位在程式碼中表示手勢門檻,然後將其轉換為實際像素。例如:
Kotlin
// The gesture threshold expressed in dp private const val GESTURE_THRESHOLD_DP = 16.0f private var gestureThreshold: Int = 0 // Convert the dps to pixels, based on density scale gestureThreshold = TypedValue.applyDimension( COMPLEX_UNIT_DIP, GESTURE_THRESHOLD_DP + 0.5f, resources.displayMetrics).toInt() // Use gestureThreshold as a distance in pixels...
Java
// The gesture threshold expressed in dp private final float GESTURE_THRESHOLD_DP = 16.0f; // Convert the dps to pixels, based on density scale int gestureThreshold = (int) TypedValue.applyDimension( COMPLEX_UNIT_DIP, GESTURE_THRESHOLD_DP + 0.5f, getResources().getDisplayMetrics()); // Use gestureThreshold as a distance in pixels...
DisplayMetrics.density
欄位會指定用於根據目前像素密度將 dp 單位轉換為像素的縮放比例係數。在 medium 像素密度的螢幕上,DisplayMetrics.density
等於 1.0,而在高密度螢幕中則等於 1.5。在超高密度螢幕中則等於 2.0,在低密度螢幕中則等於 0.75。TypedValue.applyDimension()
會使用這個數字取得目前畫面的實際像素數量。
使用預先縮放的設定值
您可以使用 ViewConfiguration
類別存取 Android 系統的常用距離、速度和時間。舉例來說,您可以使用 getScaledTouchSlop()
取得架構做為捲動門檻使用的距離 (以像素為單位):
Kotlin
private val GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).scaledTouchSlop
Java
private final int GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).getScaledTouchSlop();
無論目前像素密度為何,只要以 getScaled
前置字元開頭的 ViewConfiguration
方法,系統就能傳回能夠正確顯示的像素值。
偏好向量圖形
如果不想建立多個特定密度的圖片版本,另一個方法是建立單一向量圖形。向量圖形使用 XML 建立圖片以定義路徑和顏色,而不是使用像素點陣圖。因此,向量圖形可以縮放到任何大小,而無需縮放構件,但通常最適合用於圖示等插圖,而非相片。
向量圖形通常會以 SVG (可擴充向量圖形) 檔案提供,但 Android 不支援此格式,因此您必須將 SVG 檔案轉換為 Android 的向量可繪項目格式。
您可以使用 Android Studio 的 Vector Asset Studio 將 SVG 轉換為向量可繪項目,如下所示:
- 在「Project」視窗中的「res」目錄上按一下滑鼠右鍵,然後依序選取「New」>「Vector Asset」。
- 選取「本機檔案 (SVG、PSD)」。
找到想要匯入的檔案,並進行調整。
圖 2:使用 Android Studio 匯入 SVG。 您可能會在「Asset Studio」視窗中發現一些錯誤,指出向量可繪項目不支援檔案的某些屬性。這不會阻止您匯入檔案,並忽略不支援的屬性。
按一下「Next」。
在下一個畫面中,確認您要在專案中存放檔案的來源集,然後按一下「Finish」。
由於一個向量可繪項目可用於所有像素密度,因此這個檔案會位於預設的可繪項目目錄,如以下階層所示。您不需要使用密度專屬的目錄。
res/ drawable/ ic_android_launcher.xml
如要進一步瞭解如何建立向量圖形,請參閱向量可繪項目說明文件。
提供替代點陣圖
為了針對具有不同像素密度的裝置提供良好的圖形品質,請在應用程式中提供每個點陣圖版本 (每個像素密度級別一個),且該解析度各不相同。否則,Android 必須縮放點陣圖,使其在每個畫面上佔用相同的可見空間,進而導致縮放失真 (例如模糊)。
![顯示不同密度大小點陣圖相對大小的圖片](https://developer.android.com/static/images/screens_support/devices-density_2x.png?authuser=9&hl=zh-tw)
有幾種密度級別可供您的應用程式使用。表 1 說明可用的各種設定限定詞,以及這些限定詞適用的螢幕類型。
表 1. 適用於不同像素密度的設定限定詞
密度限定詞 | 說明 |
---|---|
ldpi |
適用於低密度 (ldpi) 螢幕的資源 (約 120 dpi)。 |
mdpi |
適用於中密度 (mdpi) 螢幕的資源 (約 160 dpi)。此為基準密度。 |
hdpi |
適用於高密度 (hdpi) 螢幕的資源 (約 240 dpi)。 |
xhdpi |
適用於 extra-high 像素密度 (xhdpi) 螢幕的資源 (約為 320 dpi)。 |
xxhdpi |
適用於 extra-extra-high 像素密度 (xxhdpi) 螢幕的資源 (約為 480 dpi)。 |
xxxhdpi |
適用於 extra-extra-extra-high 像素密度 (xxxhdpi) 螢幕的資源 (約為 640 dpi)。 |
nodpi |
適用於所有像素密度的資源,都屬於像素密度獨立資源。無論目前的螢幕密度為何,系統不會縮放以這個限定詞標記的資源。 |
tvdpi |
適用於 mdpi 與 hdpi 之間的螢幕資源,約 213dpi。這並不是「主要的」像素密度分組,主要用於電視,且大多數應用程式均不需要使用此限定詞。若認為必須提供 tvdpi 資源,請以 1.33 * mdpi 為倍數將其調整大小。例如,mdpi 螢幕的 100x100 像素圖片在 tvdpi 螢幕上會是 133x133 像素。 |
如要為不同密度建立替代點陣圖可繪項目,請遵循 6 個主要密度之間的 3:4:6:8:12:16 縮放比例。舉例來說,如果您的點陣圖可繪項目為 medium 像素密度螢幕且為 48x48 像素,則大小為:
- 36x36 (0.75x),適用於低密度 (ldpi)
- 48x48 (1.0x 基準),適用於中密度 (mdpi)
- 72x72 (1.5x),適用於高密度 (hdpi)
- 96x96 (2.0x),適用於 extra-high 像素密度 (xhdpi)
- 144x144 (3.0x),適用於 extra-extra-high 像素密度 (xxhdpi)
- 192x192 (4.0x),適用於 extra-extra-extra-high 密度 (xxxhdpi)
將產生的圖片檔放在 res/
底下的適當子目錄中:
res/ drawable-xxxhdpi/ awesome_image.png drawable-xxhdpi/ awesome_image.png drawable-xhdpi/ awesome_image.png drawable-hdpi/ awesome_image.png drawable-mdpi/ awesome_image.png
接著,當您參照 @drawable/awesomeimage
時,系統會根據螢幕的 dpi 選取適當的點陣圖。如未提供該密度的像素密度專屬資源,系統會找出下一個最相符的項目,並配合螢幕調整大小。
提示:如果您有不需要系統縮放的可繪製資源 (例如,當您在執行階段自行對圖片執行某些調整時),請將這些資源放在具有 nodpi
設定限定詞的目錄中。設有這項限定詞的資源表示與密度無關,系統不會縮放這些資源。
如要進一步瞭解其他設定限定詞,以及 Android 如何根據目前的螢幕設定選擇合適的資源,請參閱「應用程式資源總覽」。
將應用程式圖示放在 mipmap 目錄中
和其他點陣圖素材資源一樣,您需要提供特定密度的應用程式圖示。不過,部分應用程式啟動器顯示的應用程式圖示,遠大於裝置密度級別所稱的 25%。
舉例來說,如果裝置的密度級別為 xxhdpi,而您提供的應用程式圖示最大為 drawable-xxhdpi
,應用程式啟動器會放大這個圖示,使圖示看起來較不清晰。
為避免這種情況,請將所有應用程式圖示放入 mipmap
目錄,而非 drawable
目錄。與 drawable
目錄不同,無論您是否已建構密度專屬 APK,所有 mipmap
目錄都會保留在 APK 中。如此一來,啟動器應用程式就能挑選要在主畫面上顯示的最佳解析度圖示。
res/ mipmap-xxxhdpi/ launcher_icon.png mipmap-xxhdpi/ launcher_icon.png mipmap-xhdpi/ launcher_icon.png mipmap-hdpi/ launcher_icon.png mipmap-mdpi/ launcher_icon.png
以上述 xxhdpi 裝置的範例來說,您可以在 mipmap-xxxhdpi
目錄中提供更高密度的啟動器圖示。
如需圖示設計指南,請參閱「系統圖示」。
如要瞭解如何建構應用程式圖示,請參閱「使用 Image Asset Studio 建立應用程式圖示」一文。
罕見密度問題的建議做法
本節說明 Android 如何在不同像素密度上縮放點陣圖,以及如何進一步控制點陣圖在不同密度的繪製方式。除非應用程式會操控圖像,或在執行不同像素密度時發生問題,否則您可以忽略這個部分。
如果想進一步瞭解在執行階段操控圖像時可以支援多種密度,您需要瞭解系統如何協助確保點陣圖正確縮放。方法如下:
- 預先縮放資源,例如點陣圖可繪項目
系統會根據目前螢幕的密度使用應用程式中的任何密度專屬資源。如果正確的密度沒有提供資源,系統會載入預設資源,並視需要進行調整。系統會假設預設資源 (來自不含設定限定詞的目錄) 是專為基準像素密度 (mdpi) 而設計,並將這些點陣圖重新調整為適合目前像素密度的適當大小。
如果您要求了預先縮放資源的維度,系統會在縮放「之後」傳回代表該維度的值。舉例來說,針對 mdpi 螢幕設計為 50x50 像素的點陣圖,會在 hdpi 螢幕上縮放為 75x75 像素 (如果沒有 hdpi 額外資源),且系統會回報大小。
在某些情況下,您可能不希望 Android 預先調整資源。如要避免預先縮放,最簡單的方式就是將資源放在具有
nodpi
設定限定詞的資源目錄中。例如:res/drawable-nodpi/icon.png
當系統使用這個資料夾中的
icon.png
點陣圖時,不會根據目前裝置的密度縮放該點陣圖。 - 自動調整像素尺寸和座標
如要停用預先縮放的尺寸和圖片,請在資訊清單中將
android:anyDensity
設為"false"
,或者以程式輔助方式將Bitmap
設為"false"
。inScaled
在此情況下,系統會在繪圖時自動調整任何絕對像素座標和像素維度值。這樣做可確保像素定義的螢幕元素,與以基準像素密度 (mdpi) 顯示的實體大小大致相同。系統會以透明公開的方式對應用程式處理縮放作業,並將經過調整的像素尺寸回報給應用程式,而非實際像素尺寸。舉例來說,假設裝置搭載 480x800 的 WVGA 高密度螢幕,且大小與傳統 HVGA 螢幕相同,但它執行的應用程式已停用預先縮放功能。在本例中,當應用程式查詢螢幕尺寸時,系統會回報應用程式為 320x533,也就是像素密度的約略 mdpi 轉譯。
接著,當應用程式執行繪圖作業時,例如將矩形從 (10,10) 失效到 (100, 100),系統就會以適當比例縮放座標,藉此轉換座標,並將區域 (15,15) 變成 (150, 150)。如果應用程式直接操控經過調整的點陣圖,這種差異可能會造成非預期行為,但這屬於合理的取捨,是確保最佳應用程式效能。如果您遇到這種情況,請參閱將 dp 單位轉換為像素單位。
一般情況下,您不會停用預先縮放功能。如要支援多螢幕,按照本頁說明的基本技巧操作。
如果應用程式會以其他方式操控點陣圖或直接與螢幕上的像素互動,您可能需要採取額外步驟來支援不同的像素密度。舉例來說,如果您透過計算手指交叉的像素數量來回應觸控手勢,就必須使用適當的密度獨立像素值 (而非實際像素),但可以在 dp 和 px 值之間轉換。
測試所有像素密度
請使用不同像素密度的多種裝置測試應用程式,確保 UI 可正確縮放。請盡可能在實體裝置上測試;如果您無法存取適用於所有不同像素密度的實體裝置,請使用 Android Emulator。
如果您想在實體裝置上測試,但不想購買裝置,可以使用 Firebase Test Lab 存取 Google 資料中心內的裝置。