來聊一聊Android適配吧
窗外的山竹,在電線杆上多嘴 你說這一句 很有末日的感覺。山竹來了,連飯都沒得吃,簡直不要太恐怖。
說在前面
為什麼要做螢幕適配?我前幾天在郭霖大大的公眾號裡面看到一張圖片,我覺得很有說明性的。

由於Android系統的開放性,任何使用者、開發者、硬體廠商、運營商都可以對Android系統和硬體進行定製,修改成他們想要的樣子。 上圖就代表了各大品牌的手機碎片化的現狀。隨著Android裝置的增多,裝置碎片化、系統碎片化、螢幕尺寸碎片化、螢幕碎片化的程度也在不斷加深。
當Android系統、螢幕尺寸、螢幕密度出現碎片化的時候,就很容易出現同一元素在不同手機上顯示不同的問題。有的時候可能在4.3寸螢幕上面的樣子是完美的,但是當其安裝到5.7螢幕上面的就會出現很大的問題。
為了保證不同解析度、螢幕大小的使用者體驗效果一致,從而引發了適配的課題
基本概念想·
1、 畫素:
-
含義:通常所說的畫素,就是CCD/CMOS上光電感應元件的數量,一個感光元件經過感光,光電訊號轉換,A/D轉換等步驟以後,在輸出的照片上就形成一個點,我們如果把影像放大數倍,會發現這些連續色調其實是由許多色彩相近的小方點所組成,這些小方點就是構成影像的最小單位“畫素”(Pixel)。簡而言之,畫素就是手機螢幕的最小構成單元。
-
單位:px(pixel),1px = 1畫素點 一般情況下UI設計師的設計圖會以px作為統一的計量單位。
2、 解析度:
-
含義:手機在橫向、縱向上的畫素點數總和 一般描述成 寬*高 ,即橫向畫素點個數 * 縱向畫素點個數(如1080 x 1920)。
-
單位:px(pixel),1px = 1畫素點
3、 螢幕尺寸(in):
-
含義:手機對角線的物理尺寸
-
單位 英寸(inch),一英寸大約2.54cm 常見的尺寸有4.7寸、5寸、5.5寸、6寸
4、 螢幕畫素密度(dpi):
-
含義:每英寸的畫素點數。 例如每英寸內有160個畫素點,則其畫素密度為160dpi。
-
單位:dpi(dots per inch)
-
計算公式: 畫素密度 = 畫素 / 尺寸 (dpi = px / in)
-
標準螢幕畫素密度(mdpi): 每英寸長度上還有160個畫素點(160dpi),即稱為標準螢幕畫素密度(mdpi)。
-
螢幕尺寸、解析度、畫素密度三者關係:
一部手機的解析度是寬x高,螢幕大小是以寸為單位,那麼三者的關係是:
5、 密度無關畫素(dp):
-
含義:density-independent pixel,叫dp或dip,與終端上的實際物理畫素點無關
-
單位:dp,可以保證在不同螢幕畫素密度的裝置上顯示相同的效果,是安卓特有的長度單位。

6、 獨立比例畫素(sp):
-
含義:scale-independent pixel,叫sp或sip
-
單位:sp,字型大小專用單位 Android開發時用此單位設定文字大小,可根據字型大小首選項進行縮放; 推薦使用12sp、14sp、18sp、22sp作為字型大小,不推薦使用奇數和小數,容易造成精度丟失,12sp以下字型太小。
7、sp 與 dp 的區別:
-
dp只跟螢幕的畫素密度有關;
-
sp和dp很類似但唯一的區別是,Android系統允許使用者自定義文字尺寸大小(小、正常、大、超大等等),當文字尺寸是“正常”時1sp=1dp=0.00625英寸,而當文字尺寸是“大”或“超大”時,1sp>1dp=0.00625英寸。類似我們在windows裡調整字型尺寸以後的效果——視窗大小不變,只有文字大小改變。
追到android原始碼,發現系統內部用applyDimension() (路徑:android.util.TypedValue.applyDimension())將所有單位都轉換成px 再處理:
/** * Converts an unpacked complex data value holding a dimension to its final floating * point value. The two parameters <var>unit</var> and <var>value</var> * are as in {@link #TYPE_DIMENSION}. * * @param unit The unit to convert from. * @param value The value to apply the unit to. * @param metrics Current display metrics to use in the conversion -- *supplies display density and scaling information. * * @return The complex floating point value multiplied by the appropriate * metrics depending on its unit. */ public static float applyDimension(int unit, float value, DisplayMetrics metrics) { switch (unit) { case COMPLEX_UNIT_PX: return value; case COMPLEX_UNIT_DIP: return value * metrics.density; case COMPLEX_UNIT_SP: return value * metrics.scaledDensity; case COMPLEX_UNIT_PT: return value * metrics.xdpi * (1.0f/72); case COMPLEX_UNIT_IN: return value * metrics.xdpi; case COMPLEX_UNIT_MM: return value * metrics.xdpi * (1.0f/25.4f); } return 0; } 可以發現dp和sp的區別在於density和scaledDensity兩個值上; /** * The logical density of the display.This is a scaling factor for the * Density Independent Pixel unit, where one DIP is one pixel on an * approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen), * providing the baseline of the system's display. Thus on a 160dpi screen * this density value will be 1; on a 120 dpi screen it would be .75; etc. * * <p>This value does not exactly follow the real screen size (as given by * {@link #xdpi} and {@link #ydpi}, but rather is used to scale the size of * the overall UI in steps based on gross changes in the display dpi.For * example, a 240x320 screen will have a density of 1 even if its width is * 1.8", 1.3", etc. However, if the screen resolution is increased to * 320x480 but the screen size remained 1.5"x2" then the density would be * increased (probably to 1.5). * * @see #DENSITY_DEFAULT */ public float density; /** * A scaling factor for fonts displayed on the display.This is the same * as {@link #density}, except that it may be adjusted in smaller * increments at runtime based on a user preference for the font size. */ public float scaledDensity;
螢幕適配的方案:
-
圖片適配
關於圖片適配呢?我兩個點我要提及:
1、 資源位置:
前面的介紹我們可以知道:在AndroidStudio的資源目錄res下有五個層級圖片資料夾,分別用來存放不同解析度的圖片:
drawable-ldpi :低解析度(用的少了,一般不再用)
drawable-mdpi:中解析度
drawable-hdpi:高解析度
drawable-xdpi:較高解析度
drawable-xxdpi:超級高解析度
drawable-xxxhpi:頂級解析度
在對應的資料夾下放置不同解析度的圖片就可以很好的對圖片進行適配。
隨著螢幕越來越大,推薦xxdpi的一套切圖,這樣就可以向下和向上相容,節省資源。
2、 圖片拉伸:
如果對於Android有一定的瞭解的開發者,都可能會曉得imageview有一個scaleType用來適配圖片的大小:
-
android:scaleType=“center” 保持原圖的大小,顯示在ImageView的中心。當原圖的size大於ImageView的size時,多出來的部分被截掉。
-
android:scaleType=“center_inside” 以原圖正常顯示為目的,如果原圖大小大於ImageView的size,就按照比例縮小原圖的寬高,居中顯示在ImageView中。如果原圖size小於ImageView的size,則不做處理居中顯示圖片。
-
android:scaleType=“center_crop” 以原圖填滿ImageView為目的,如果原圖size大於ImageView的size,則與center_inside一樣,按比例縮小,居中顯示在ImageView上。如果原圖size小於ImageView的size,則按比例拉昇原圖的寬和高,填充ImageView居中顯示。
-
android:scaleType=“matrix” 不改變原圖的大小,從ImageView的左上角開始繪製,超出部分做剪下處理。
-
androd:scaleType=“fit_xy” 把圖片按照指定的大小在ImageView中顯示,拉伸顯示圖片,不保持原比例,填滿ImageView.
-
android:scaleType=“fit_start” 把原圖按照比例放大縮小到ImageView的高度,顯示在ImageView的start(前部/上部)。
-
android:sacleType=“fit_center” 把原圖按照比例放大縮小到ImageView的高度,顯示在ImageView的center(中部/居中顯示)。
-
android:scaleType=“fit_end” 把原圖按照比例放大縮小到ImageView的高度,顯示在ImageVIew的end(後部/尾部/底部)
-
佈局適配
關於佈局適配呢?我也有兩點要進行提及:
1、 建議:
不要使用絕對佈局,使用相對佈局和線性佈局來代替絕對佈局。
2、 資源位置:
在螢幕適配中,我們也可以和圖片一樣:根據手機大小不一樣的手機建立不同的佈局,比如說建立兩個資料夾:
- layout-800 * 480
- layout-1280 * 720
手機會根據解析度去找設定的不同大小的layout的佈局。
-
Contractlayout適配:
我在這裡呢? 就不講這個了,詳情呢?請檢視我前面的文章 ConstraintLayout用法詳解 。順便也關注一下
-
尺寸適配(dimens適配)
在螢幕適配中,我們也可以和圖片一樣:根據手機大小不一樣的手機建立不同的dimens,比如說建立兩個資料夾:
- values-400*320
- values-800*480
手機會根據解析度去找設定的不同大小的dimens的引數。
-
權重適配:
當佈局佔滿螢幕寬或高的時候,子佈局可以使用權重適配。例如LinearLayout中的weight屬性。
-
dp dp dp
android的單位dp本身就有適配的功能,如下圖所示:

所以用好dp,本身就是一種適配,下面附一下dp轉px的方法 供日後檢視:
fun Int.dp2Px(context: Context): Int = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), context.resources.displayMetrics ).toInt() fun Int.px2Dp(context: Context): Int = (this.toFloat() / context.resources.displayMetrics.density).toInt() fun Int.sp2Px(context: Context): Int = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, this.toFloat(), context.resources.displayMetrics ).toInt() fun Int.px2Sp(context: Context): Int = (this.toFloat() / context.resources.displayMetrics.scaledDensity).toInt() fun Float.dp2Px(context: Context): Int = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, this, context.resources.displayMetrics ).toInt() fun Float.sp2Px(context: Context): Int = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, this, context.resources.displayMetrics ).toInt()
-
程式碼的適配:
//獲取手機螢幕的寬和高 val widthPixels = resources.displayMetrics.widthPixels val heightPixels = resources.displayMetrics.heightPixels //給button設定寬和高 val layoutParams = bt_main_button.getLayoutParams() layoutParams.width = widthPixels / 2 layoutParams.height = heightPixels / 2 bt_main_button.setLayoutParams(layoutParams)
由上面程式碼可知:我們可以計算螢幕的大小 來確定控制元件的大小 這樣就能達到適配的效果。
說在最後:
適配是一門大學問,無論是剛入行的新手 還是入行多年的老手 對於適配其實或多或少都有點怵的。算了,我不知道嗶嗶什麼了,窗外的那個吊車被颱風吹到了我的窗前,我現在有點虛 時刻都在注意著它的“走位”,生怕他一個e閃將我留在這裡。不寫了不寫了。