Android 屏幕適配,那些你必須掌握的稀碎知識(shí)點(diǎn)

背景

屏幕適配一直是 Android 中非常重要的環(huán)節(jié),但是也涉及很多瑣碎的知識(shí)點(diǎn),本文將帶你深入分析屏幕適配的各個(gè)環(huán)節(jié),看完本文您將能夠回答以下問(wèn)題:

  1. dpi,ppi,density,dp,px 有什么區(qū)別?

  2. 為什么適配時(shí)要把圖片放在最大分辨率的目錄下呢?

  3. 同一張圖片放在不同的 drawable 目錄下,加載進(jìn)內(nèi)存后會(huì)有寬高尺寸的變化嗎?

  4. 圖片放在 xhdpi 目錄下,分別用不同屏幕像素密度的手機(jī)加載進(jìn)內(nèi)存后會(huì)有寬高尺寸的變化嗎?

  5. 為啥在硬盤(pán)上存儲(chǔ)只需要 77.11k,放到內(nèi)存里面就需要 30 多 M 呢?

  6. 為什么最好只將應(yīng)用圖標(biāo)放在 mipmap 目錄中?

  7. 手動(dòng)修改屏幕像素密度會(huì)不會(huì)影響 app 加載固定分辨率的文件夾?

  8. 主流適配方案的對(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è) Viewdp 值?不現(xiàn)實(shí),在每個(gè)設(shè)備上都要通過(guò)代碼動(dòng)態(tài)計(jì)算 Viewdp 值,工作量太大。

如果每個(gè) Viewdp 值是固定不變的,那我們只要保證每個(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ù)更新中...

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容