背景
屏幕適配一直是 Android 中非常重要的環(huán)節(jié),但是也涉及很多瑣碎的知識(shí)點(diǎn),本文將帶你深入分析屏幕適配的各個(gè)環(huán)節(jié),看完本文您將能夠回答以下問(wèn)題:
dpi,ppi,density,dp,px 有什么區(qū)別?
為什么適配時(shí)要把圖片放在最大分辨率的目錄下呢?
同一張圖片放在不同的 drawable 目錄下,加載進(jìn)內(nèi)存后會(huì)有寬高尺寸的變化嗎?
圖片放在 xhdpi 目錄下,分別用不同屏幕像素密度的手機(jī)加載進(jìn)內(nèi)存后會(huì)有寬高尺寸的變化嗎?
為啥在硬盤(pán)上存儲(chǔ)只需要 77.11k,放到內(nèi)存里面就需要 30 多 M 呢?
為什么最好只將應(yīng)用圖標(biāo)放在 mipmap 目錄中?
手動(dòng)修改屏幕像素密度會(huì)不會(huì)影響 app 加載固定分辨率的文件夾?
主流適配方案的對(duì)比?
dpi,ppi,density,dp,px 有什么區(qū)別?
DPI/PPI DPI = Dots Per Inch,PPI = Pixel Per Inch 兩個(gè)參數(shù)的區(qū)別就在于 Dot 和 Pixel的區(qū)別,dot值的是顯示器上每一個(gè)物理的點(diǎn),而 pixel 指的是屏幕分辨率中的最小單位。這個(gè)兩個(gè)難道會(huì)不一樣么?會(huì)!當(dāng)一個(gè)像素需要多于一個(gè)屏幕上的物理點(diǎn)來(lái)顯示的時(shí)候 dot 就跟 pixel 不一樣了。這個(gè)有另一個(gè)叫法叫做dppx(dot per pixel),即每個(gè)像素中有多少個(gè)點(diǎn)。大部分的顯示器中一個(gè)像素即一個(gè)點(diǎn),但目前一些比較好的屏幕和一些手機(jī)屏幕中 dppx 會(huì)大于1。比如說(shuō) Mac Retina,iPhone,HTC One 等。總的來(lái)說(shuō)我們一般針對(duì) Android 手機(jī)說(shuō)的 dpi 和 ppi 是等價(jià)的。所有我們就把他們統(tǒng)稱為屏幕像素密度了。屏幕密度越低在給定物理區(qū)域的像素就會(huì)越少,Android 中通用密度如下:
ldpi |
適用于低密度 (ldpi) 屏幕 (~ 120dpi) 的資源。 |
|---|---|
mdpi |
適用于中密度 (mdpi) 屏幕 (~ 160dpi) 的資源(這是基準(zhǔn)密度)。 |
hdpi |
適用于高密度 (hdpi) 屏幕 (~ 240dpi) 的資源。 |
xhdpi |
適用于加高 (xhdpi) 密度屏幕 (~ 320dpi) 的資源。 |
xxhdpi |
適用于超超高密度 (xxhdpi) 屏幕 (~ 480dpi) 的資源。 |
xxxhdpi |
適用于超超超高密度 (xxxhdpi) 屏幕 (~ 640dpi) 的資源。 |
nodpi |
適用于所有密度的資源。這些是與密度無(wú)關(guān)的資源。無(wú)論當(dāng)前屏幕的密度是多少,系統(tǒng)都不會(huì)縮放以此限定符標(biāo)記的資源。 |
tvdpi |
適用于密度介于 mdpi 和 hdpi 之間的屏幕(約 213dpi)的資源。這不屬于“主要”密度組。它主要用于電視,而大多數(shù)應(yīng)用都不需要它。對(duì)于大多數(shù)應(yīng)用而言,提供 mdpi 和 hdpi 資源便已足夠,系統(tǒng)將視情況對(duì)其進(jìn)行縮放。如果您發(fā)現(xiàn)有必要提供 tvdpi 資源,應(yīng)按一個(gè)系數(shù)來(lái)確定其大小,即 1.33*mdpi。例如,如果某張圖片在 mdpi 屏幕上的大小為 100px x 100px,那么它在 tvdpi 屏幕上的大小應(yīng)該為 133px x 133px。 |
anydpi |
此限定符適合所有屏幕密度,其優(yōu)先級(jí)高于其他限定符。這非常適用于矢量可繪制對(duì)象。此項(xiàng)為 API 級(jí)別 21 中的新增配置 |
nnndpi |
用于表示非標(biāo)準(zhǔn)密度,其中 *nnn* 是正整數(shù)屏幕密度。此限定符不適用于大多數(shù)情況。使用標(biāo)準(zhǔn)密度存儲(chǔ)分區(qū),可顯著減少因支持市場(chǎng)上各種設(shè)備屏幕密度而產(chǎn)生的開(kāi)銷。 |
要針對(duì)不同的密度創(chuàng)建備用可繪制位圖資源,您應(yīng)遵循六種主要密度之間的 3:4:6:8:12:16 縮放比。例如,如果您有一個(gè)可繪制位圖資源,它在中密度屏幕上的大小為 48x48 像素,那么它在其他各種密度的屏幕上的大小應(yīng)該為:
- 36x36 (0.75x) - 低密度 (ldpi)
- 48x48(1.0x 基準(zhǔn))- 中密度 (mdpi)
- 72x72 (1.5x) - 高密度 (hdpi)
- 96x96 (2.0x) - 超高密度 (xhdpi)
- 144x144 (3.0x) - 超超高密度 (xxhdpi)
- 192x192 (4.0x) - 超超超高密度 (xxxhdpi)
density:通過(guò)代碼 context.getResources().getDisplayMetrics().density獲取的“density”值。而通過(guò)該方法獲取到的該值,實(shí)際上是等價(jià)于“dpi / 160”的一個(gè)結(jié)果值。
dp:密度無(wú)關(guān)像素,1 dp 約等于中密度屏幕(160dpi;“基準(zhǔn)”密度)上的 1 像素。如果要在密度不同的屏幕上顯示大致相同的可見(jiàn)尺寸,應(yīng)該使用密度無(wú)關(guān)像素 dp。
- 在分辨率 320*480(即 dpi 為 160)的設(shè)備下,則 160dp 等價(jià)于 160px,按鈕占屏幕寬的一半。
- 在分辨率 640*960(即 dpi 為 320)的設(shè)備下,則 160dp 等價(jià)于 320px,按鈕依然占屏幕寬的一半。
px:其實(shí)就是像素單位,比如我們通常說(shuō)的手機(jī)分辨列表 800400 都是 px 的單位*
dp,px 與160之間存在著某種規(guī)律:“1dp = (dpi / 160)px”,dp = px / density,Android 提供 density 的目的,歸根結(jié)底就是為了做屏幕的適配。
同一張圖片放在不同的 drawable 目錄下,加載進(jìn)內(nèi)存后會(huì)有寬高尺寸的變化嗎?
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/dev"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
復(fù)制代碼
Java 代碼:
Drawable drawable = imageView.getDrawable();
if (drawable != null) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
Log.d(TAG, " width = " + bitmap.getWidth() + " height = " + bitmap.getHeight());
}
復(fù)制代碼
原圖大小:352 * 484
放在 320dpi(xhdpi) 的設(shè)備上的 xhdpi 的目錄中,獲取到圖片的寬高為 352 * 484,因?yàn)樵O(shè)備為 xhdpi 和 xhdpi 目錄相對(duì)應(yīng),所以獲取到圖片的寬高自然就沒(méi)有變化了。
放在 mdpi 目錄中,獲取到圖片的寬高為 704 * 968,可以這樣理解,圖片放在mdpi 目錄中,碰到 xhdpi 的手機(jī),系統(tǒng)默認(rèn) mdpi 中的圖片都比較小,就要進(jìn)行放大,因?yàn)?xhdpi 是 mdpi 的兩倍,所以 Bitmap 的寬高都放大了兩倍。
放在xxhpi 目錄中,獲取到圖片的寬高為 235 * 323,253 = 352 * (2/3) 323 = 484 * (2/3)
同時(shí)需要注意的是 drawable 和 drawable-mdpi 是一樣的大小,是因?yàn)?drawable-mdpi 是系統(tǒng)默認(rèn)的像素密度,其他像素密度都以它為基數(shù),當(dāng)只在 drawable 中存在圖片時(shí),如果使用該圖片,那么將按照 drawable-mdpi 的放縮比例進(jìn)行縮放。
為什么適配時(shí)要把圖片放在最大分辨率的目錄下呢?
前面說(shuō)過(guò)內(nèi)存占用計(jì)算公式:寬 * 高 * 單位像素占用字節(jié),放在低分辨率的手機(jī)上,Bitmap 的寬高反而變大了,那內(nèi)存也增加了嗎?答案是的,因?yàn)閮?nèi)存只和圖片的寬高,單位像素占用字節(jié)有關(guān)。同一張圖片,放在不同目錄下,會(huì)生成不同大小的 Bitmap,Bitmap 的寬度和高度越大,占用的內(nèi)存就越大,放在越低分辨率中占用的內(nèi)存越大,所以為了適配(如果只有一個(gè)文件夾的話),應(yīng)該盡量把圖片放在 xxxhdpi 下面。
圖片放在 xhdpi 目錄下,分別用不同屏幕像素密度的手機(jī)加載進(jìn)內(nèi)存后會(huì)有寬高尺寸的變化嗎?
原圖 Bitmap 大?。?52 * 484,設(shè)備 320dpi(xhdpi)
放在 320dpi(xhdpi) 的設(shè)備上的 xhdpi 的目錄中,獲取到圖片的寬高為 352 * 484,因?yàn)樵O(shè)備為 xhdpi 和 xhdpi 目錄相對(duì)應(yīng),所以獲取到圖片的寬高自然就沒(méi)有變化了。
原圖 Bitmap 大?。?52 * 484,設(shè)備 480dpi(xxhdpi),放在xhdpi 目錄中
當(dāng)使用 xxhdpi 的設(shè)備后,獲取到圖片的寬高為 528 * 726,也就是寬和高分別乘以 3/2,可以看出,在碰到更高分辨率(屏幕密度的手機(jī)),圖片的寬高會(huì)根據(jù)比例進(jìn)行放大,如果使用 mdpi 的設(shè)備下則縮小一半;
如果這個(gè)時(shí)候我把圖片放在 mdpi 的目錄下(原始大?。褂?xxhdpi 設(shè)備,獲取到圖片的寬高為 1056 * 1452,放大的倍數(shù)為 3 倍。
小結(jié)
在同一個(gè)設(shè)備上,圖片放在依次放在由低到高的分辨率目錄中,圖片的 Bitmap 的大小不斷減小。
在同一個(gè)分辨率目錄中,依次運(yùn)行在由低到高的分辨率設(shè)備上,圖片的 Bitmap 的大小不斷增加。
為啥在硬盤(pán)上存儲(chǔ)只需要77.11k,放到內(nèi)存里面就需要30多M呢?
因?yàn)檫@根本不是一回事呀~
存放在硬盤(pán)上的圖片文件,會(huì)根據(jù)各自的壓縮規(guī)則進(jìn)行壓縮,比如 Jpeg 這種有損壓縮的圖片格式,最常使用可變字長(zhǎng)編碼的哈弗曼編碼,會(huì)使用哈弗曼樹(shù),也就是最優(yōu)二叉樹(shù),根據(jù)某些數(shù)據(jù)出現(xiàn)的頻率對(duì)數(shù)據(jù)段編碼,從而減少占用的硬盤(pán)大小。
比如說(shuō) “10111” 這個(gè)序列在圖片的二進(jìn)制數(shù)據(jù)中出現(xiàn)的概率最大,那我們可以用“01”來(lái)代替這一段數(shù)據(jù),原來(lái)5位的數(shù)據(jù),用2位就可以表示了,這就是壓縮率60%。當(dāng)然這只是打個(gè)比方,在實(shí)際操作中需要考慮“異前綴原則”等編碼的基本原則。
而如果把圖像讀取到內(nèi)存中就不一樣了,因?yàn)槲覀冃枰恳粋€(gè)像素都能在屏幕上顯示,所以會(huì)把每個(gè)像素點(diǎn)都加載至內(nèi)存中,不會(huì)對(duì)相同像素進(jìn)行壓縮或者是替換,所以你也應(yīng)該能明白前面提到的Bitmap占用內(nèi)存大小的計(jì)算公式的由來(lái)了。
修改屏幕像素密度會(huì)不會(huì)影響 app 加載固定分辨率的文件夾?
經(jīng)測(cè)試,在不同分辨率下放置內(nèi)容不同的圖片但名稱相同的圖片,修改屏幕像素密度并不會(huì)影響 app 加載固定分辨率的文件夾,也就是說(shuō)還是加載相同內(nèi)容的圖片。
adb shell wm density //查看屏幕像素密度
adb shell wm density 640 //設(shè)置屏幕像素密度為 640
adb shell wm density reset //重置為初始像素密度
復(fù)制代碼
最好只將應(yīng)用圖標(biāo)放在 mipmap 目錄中
mipmap 翻譯過(guò)來(lái)就是紋理映射技術(shù),mipmap 文件夾下的圖標(biāo)會(huì)通過(guò) Mipmap 紋理技術(shù)進(jìn)行優(yōu)化。Mipmap 紋理技術(shù)是目前解決紋理分辨率與視點(diǎn)距離關(guān)系的最有效途徑,它會(huì)先將圖片壓縮成很多逐漸縮小的圖片,例如一張 64 * 64的圖片,會(huì)產(chǎn)生64 * 64,32 * 32,16 * 16,8 * 8,4 * 4,2 * 2,1 * 1的 7 張圖片,當(dāng)屏幕上需要繪制像素點(diǎn)為20 * 20 時(shí),程序只是利用 32 * 32 和 16 * 16 這兩張圖片來(lái)計(jì)算出即將顯示為 20 * 20 大小的一個(gè)圖片,這比單獨(dú)利用 32 * 32 的那張?jiān)计?jì)算出來(lái)的圖片效果要好得多,速度也更快。
為什么要把應(yīng)用圖標(biāo)放在 mipmap 目錄中,下面引用官方的解釋:
與其他所有位圖資源一樣,對(duì)于應(yīng)用圖標(biāo),您也需要提供特定于密度的版本。不過(guò),某些桌面應(yīng)用顯示的應(yīng)用圖標(biāo)會(huì)比設(shè)備的密度級(jí)別所要求的大差不多 25%(However, some app launchers display your app icon as much as 25% larger than what's called for by the device's density bucket)。
例如,如果設(shè)備的密度存儲(chǔ)分區(qū)為 xxhdpi,而您提供的最大應(yīng)用圖標(biāo)密度級(jí)別為 drawable-xxhdpi,則啟動(dòng)器應(yīng)用會(huì)放大此圖標(biāo),這會(huì)導(dǎo)致它看起來(lái)不太清晰。因此,您應(yīng)在 mipmap-xxxhdpi 目錄中提供一個(gè)密度更高的啟動(dòng)器圖標(biāo),而后啟動(dòng)器便可改用 xxxhdpi 資源。
由于應(yīng)用圖標(biāo)可能會(huì)像這樣放大,因此您應(yīng)將所有應(yīng)用圖標(biāo)都放在 mipmap 目錄中,而不是放在 drawable 目錄中。與 drawable 目錄不同,所有 mipmap 目錄都會(huì)保留在 APK 中,即使您構(gòu)建特定于密度的 APK 也是如此。這樣,啟動(dòng)器應(yīng)用便可選取要顯示在主屏幕上的最佳分辨率圖標(biāo)。
注意
個(gè)人感覺(jué)應(yīng)該在不同分辨率的 mipmap 文件夾下分別提供大小不同的圖片,然后通過(guò)紋理映射技術(shù)顯示 mipmap 中沒(méi)有提供的圖片時(shí),顯示圖片又快又好。
所有 mipmap 目錄都會(huì)保留在 APK 中,即使您構(gòu)建特定于密度的 APK 也是如此,也就是說(shuō)就算配置了類似 resConfigs "xhdpi" 這種方式,對(duì) mipmap 目錄下的文件沒(méi)有任何作用,所以最好就是只把應(yīng)用圖標(biāo)放進(jìn)去就行了。因?yàn)閼?yīng)用圖標(biāo)占用的體積也不大,同時(shí)應(yīng)用圖標(biāo)的清晰顯示也至關(guān)重要。
市面上主流適配方案(來(lái)自 JessYan 大佬的文章)
density 在每個(gè)設(shè)備上都是固定的,DPI / 160 = density,屏幕的總 px 寬度 / density = 屏幕的總 dp 寬度,由于 Android 的碎片化問(wèn)題,大部分市面上的 Android 設(shè)備的寬高比都不一致,這時(shí)我們只以高或?qū)捚渲械囊粋€(gè)作為基準(zhǔn)進(jìn)行適配,就會(huì)有效的避免布局在高寬比不一致的屏幕上出現(xiàn)變形的問(wèn)題。
今日頭條適配方案
原理
- 設(shè)備 1,屏幕寬度為 1080px,480DPI,屏幕總 dp 寬度為 1080 / (480 / 160) = 360dp
- 設(shè)備 2,屏幕寬度為 1440px,560DPI,屏幕總 dp 寬度為 1440 / (560 / 160) = 411dp
可以看到屏幕的總 dp 寬度在不同的設(shè)備上是會(huì)變化的,但是我們?cè)诓季种刑顚?xiě)的 dp 值卻是固定不變的
這會(huì)導(dǎo)致什么呢?假設(shè)我們布局中有一個(gè) View 的寬度為 100dp,在設(shè)備 1 中 該 View 的寬度占整個(gè)屏幕寬度的 27.8% (100 / 360 = 0.278)
但在設(shè)備 2 中該 View 的寬度就只能占整個(gè)屏幕寬度的 24.3% (100 / 411 = 0.243),可以看到這個(gè) View 在像素越高的屏幕上,dp 值雖然沒(méi)變,但是與屏幕的實(shí)際比例卻發(fā)生了較大的變化,所以肉眼的觀看效果,會(huì)越來(lái)越小,這就導(dǎo)致了傳統(tǒng)的填寫(xiě) dp 的屏幕適配方式產(chǎn)生了較大的誤差。
這時(shí)我們要想完美適配,那就必須保證這個(gè) View 在任何分辨率的屏幕上,與屏幕的比例都是相同的。
這時(shí)我們?cè)撛趺醋瞿??改變每個(gè) View 的 dp 值?不現(xiàn)實(shí),在每個(gè)設(shè)備上都要通過(guò)代碼動(dòng)態(tài)計(jì)算 View 的 dp 值,工作量太大。
如果每個(gè) View 的 dp 值是固定不變的,那我們只要保證每個(gè)設(shè)備的屏幕總 dp 寬度不變,就能保證每個(gè) View 在所有分辨率的屏幕上與屏幕的比例都保持不變,從而完成等比例適配,并且這個(gè)屏幕總 dp 寬度如果還能保證和設(shè)計(jì)圖的寬度一致的話,那我們?cè)诓季謺r(shí)就可以直接按照設(shè)計(jì)圖上的尺寸填寫(xiě) dp 值
屏幕的總 px 寬度 / density = 屏幕的總 dp 寬度
在這個(gè)公式中我們要保證 屏幕的總 dp 寬度 和 設(shè)計(jì)圖總寬度 一致,并且在所有分辨率的屏幕上都保持不變,我們需要怎么做呢?屏幕的總 px 寬度 每個(gè)設(shè)備都不一致,這個(gè)值是肯定會(huì)變化的,這時(shí)今日頭條的公式就派上用場(chǎng)了。
當(dāng)前設(shè)備屏幕總寬度(單位為像素)/ 設(shè)計(jì)圖總寬度(單位為 dp) = density
這個(gè)公式就是把上面公式中的 屏幕的總 dp 寬度 換成 設(shè)計(jì)圖總寬度,原理都是一樣的,只要 density 根據(jù)不同的設(shè)備進(jìn)行實(shí)時(shí)計(jì)算并作出改變,就能保證 設(shè)計(jì)圖總寬度 不變,也就完成了適配。
總得來(lái)說(shuō)就是 density 修改為【當(dāng)前設(shè)備屏幕總寬度(單位為像素)/ 設(shè)計(jì)圖總寬度(單位為 dp)】 ,總的 dp 數(shù)發(fā)生了改變(比如都是設(shè)計(jì)圖的 375),View 設(shè)置的dp 保持不變,保持 View 占用總屏幕比例不變。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn)是代碼侵入性低,試錯(cuò)成本接近于 0,但是修改的 density 是全局的,當(dāng)某個(gè)系統(tǒng)控件或三方庫(kù)控件的設(shè)計(jì)圖尺寸和和我們項(xiàng)目自身的設(shè)計(jì)圖尺寸差距非常大時(shí),這個(gè)問(wèn)題就越嚴(yán)重。(解決方案可以按 Activity 為單位,取消當(dāng)前 Activity 的適配效果,改用其他的適配方案)。
SmallestWidth 限定符適配方案(最小寬度限定符適配方案)
說(shuō)到這個(gè),肯定會(huì)有人提起寬高限定符屏幕適配方案,可以把 SmallestWidth 限定符適配當(dāng)做寬高限定符適配方案的升級(jí)版。smallestWidth 限定符屏幕適配方案 只是把 dimens.xml 文件中的值從 px 換成了 dp,原理和使用方式都是沒(méi)變的。
├── src/main
│ ├── res
│ ├── ├──values
│ ├── ├──values-sw320dp
│ ├── ├──values-sw360dp
│ ├── ├──values-sw400dp
│ ├── ├──values-sw411dp
│ ├── ├──values-sw480dp
│ ├── ├──...
│ ├── ├──values-sw600dp
│ ├── ├──values-sw640dp
復(fù)制代碼
原理
其實(shí) smallestWidth 限定符屏幕適配方案 的原理也很簡(jiǎn)單,開(kāi)發(fā)者先在項(xiàng)目中根據(jù)主流屏幕的 最小寬度 (smallestWidth) 生成一系列 values-swdp 文件夾 (含有 dimens.xml 文件),當(dāng)把項(xiàng)目運(yùn)行到設(shè)備上時(shí),系統(tǒng)會(huì)根據(jù)當(dāng)前設(shè)備屏幕的 最小寬度 (smallestWidth) 去匹配對(duì)應(yīng)的 values-swdp 文件夾,而對(duì)應(yīng)的 values-swdp 文件夾中的 dimens.xml 文字中的值,又是根據(jù)當(dāng)前設(shè)備屏幕的 最小寬度 (smallestWidth) 而定制的,所以一定能適配當(dāng)前設(shè)備。
如果系統(tǒng)根據(jù)當(dāng)前設(shè)備屏幕的 最小寬度 (smallestWidth) 沒(méi)找到對(duì)應(yīng)的 values-swdp 文件夾,則會(huì)去尋找與之 最小寬度 (smallestWidth) 相近的 values-swdp 文件夾,系統(tǒng)只會(huì)尋找小于或等于當(dāng)前設(shè)備 最小寬度 (smallestWidth) 的 values-swdp,這就是優(yōu)于 寬高限定符屏幕適配方案 的容錯(cuò)率,并且也可以少生成很多 values-swdp 文件夾,減輕 App 的體積。
假如 最小寬度 的值是 360 dp (1080 / (480 / 160) = 360)
現(xiàn)在我們已經(jīng)算出了當(dāng)前設(shè)備的最小寬度是 360 dp,我們曉得系統(tǒng)會(huì)根據(jù)這個(gè) 最小寬度 幫助我們匹配到 values-sw360dp 文件夾下的 dimens.xml 文件,如果項(xiàng)目中沒(méi)有 values-sw360dp 這個(gè)文件夾,系統(tǒng)才會(huì)去匹配相近的 values-swdp 文件夾
smallestWidth 限定符屏幕適配方案 的原理也同樣是按百分比進(jìn)行布局,如果在布局中,一個(gè) View 的寬度引用 dp_100,那不管運(yùn)行到哪個(gè)設(shè)備上,這個(gè) View 的寬度都是當(dāng)前設(shè)備屏幕總寬度的 360分之100,前提是項(xiàng)目提供有當(dāng)前設(shè)備屏幕對(duì)應(yīng)的 values-swdp,如果沒(méi)有對(duì)應(yīng)的 values-swdp,就會(huì)去尋找相近的 values-swdp,這時(shí)就會(huì)存在誤差了。
由于該方案是以最小寬度進(jìn)行適配的,加入設(shè)置了 360 份,但是當(dāng)想要設(shè)置的高度大過(guò) 360 份時(shí),應(yīng)該如何設(shè)置?
這里邊的數(shù)字是可以自己定義的,比如設(shè)計(jì)圖的寬度是 375,手機(jī)為 1920*1080,dpi=480, 那么此時(shí) values-sw360dp 中的 dimens.xml 文件 375 處生成的值都等于 360 那么 361.... 362.... 這里的值都是可以加的 此文件下不止到375處結(jié)束,什么時(shí)候結(jié)束都是自己可以控制的! 可以通過(guò)修改插件,繼續(xù)生成到一定量的值。
其實(shí) smallestWidth 限定符屏幕適配方案 的原理和 今日頭條屏幕適配方案 挺像的,今日頭條屏幕適配方案 是根據(jù)屏幕的寬度或高度動(dòng)態(tài)調(diào)整每個(gè)設(shè)備的 density (每 dp 占當(dāng)前設(shè)備屏幕多少像素),而 smallestWidth 限定符屏幕適配方案 同樣是根據(jù)屏幕的寬度動(dòng)態(tài)調(diào)整每個(gè)設(shè)備 每份占的 dp 值。
優(yōu)缺點(diǎn)
非常穩(wěn)定,不會(huì)有任何性能損耗,適配范圍可以自由控制,不會(huì)影響其他第三方庫(kù),接入成本也很低。但是侵入性很強(qiáng),需要在適配效果和空間占用做取舍,不能自動(dòng)支持橫豎屏切換時(shí)的適配,如上文所說(shuō),如果想自動(dòng)支持橫豎屏切換時(shí)的適配,需要使用 values-wdp 或 屏幕方向限定符 再生成一套資源文件,這樣又會(huì)再次增加 App 的體積。
AndroidAutoSize
AndroidAutoSize 是根據(jù)今日頭條適配方案進(jìn)行優(yōu)化的,AndroidAutoSize 有兩種類型的布局單位可以選擇,一個(gè)是 主單位 (dp、sp),一個(gè)是 副單位 (pt、in、mm),兩種單位面向的應(yīng)用場(chǎng)景都有不同,也都有各自的優(yōu)缺點(diǎn)。
主單位: 使用 dp、sp 為單位進(jìn)行布局,侵入性最低,會(huì)影響其他三方庫(kù)頁(yè)面、三方庫(kù)控件以及系統(tǒng)控件的布局效果,但 AndroidAutoSize 也通過(guò)這個(gè)特性,使用 ExternalAdaptManager 實(shí)現(xiàn)了在不修改三方庫(kù)源碼的情況下適配三方庫(kù)的功能。
副單位: 使用 pt、in、mm 為單位進(jìn)行布局,侵入性高,對(duì)老項(xiàng)目的支持比較好,不會(huì)影響其他三方庫(kù)頁(yè)面、三方庫(kù)控件以及系統(tǒng)控件的布局效果,可以徹底的屏蔽修改 density 所造成的所有未知和已知問(wèn)題,但這樣 AndroidAutoSize 也就無(wú)法對(duì)三方庫(kù)進(jìn)行適配。
原理
至于自動(dòng)運(yùn)行解析是運(yùn)用 ContentProvider,在它的 onCreate 中啟動(dòng)該框架,執(zhí)行時(shí)期比 Application#onCreate 還靠前。至于多線程的初始化可以查看原文進(jìn)行操作。
如何適配三方庫(kù)界面?(前提是三方庫(kù)頁(yè)面的布局使用的是 dp 和 sp, 如果布局全部使用的 px, 那 AndroidAutoSize 也將無(wú)能為力)
在使用主單位時(shí)可以使用 ExternalAdaptManager 來(lái)實(shí)現(xiàn)在不修改三方庫(kù)源碼的情況下,適配三方庫(kù)的所有頁(yè)面 (Activity、Fragment)。
通過(guò) ExternalAdaptManager#addExternalAdaptInfoOfActivity(Class, ExternalAdaptInfo) 將需要自定義的類和自定義適配參數(shù)添加進(jìn)方法即可替代實(shí)現(xiàn) CustomAdapt 的方式,這里 展示了使用方式,以及詳細(xì)的注釋。
通過(guò) ExternalAdaptManager#addCancelAdaptOfActivity(Class) 將需要取消適配的類添加進(jìn)方法即可替代實(shí)現(xiàn) CancelAdapt 的方式,這里 也展示了使用方式,以及詳細(xì)的注釋。
優(yōu)缺點(diǎn)
算是對(duì)今日頭條方案方案的全面擴(kuò)展和優(yōu)化,解決今日頭條方案的痛點(diǎn),同時(shí)更加靈活,目前來(lái)看是最全面的方案。
總結(jié)
屏幕適配其實(shí)是個(gè)很瑣碎的知識(shí)點(diǎn),能夠真正將上面這些知識(shí)點(diǎn)串聯(lián)起來(lái)的人并不多,本文參考了許多大佬的文章,感謝他們。喜歡的點(diǎn)個(gè)贊吧~
本文在開(kāi)源項(xiàng)目:https://github.com/Android-Alvin/Android-LearningNotes 中已收錄,里面包含不同方向的自學(xué)編程路線、面試題集合/面經(jīng)、及系列技術(shù)文章等,資源持續(xù)更新中...