螢幕適配升級之今日頭條總結
因為Android系統的碎片化,手機解析度和尺寸的多樣化所以開發中經常需要進行螢幕適配。
看了今日頭條的技術部落格之螢幕適配覺得很NB,看完之後,學到很多,以下作為基礎部分回顧以及對適配方案進行總結 這是今日頭條傳送門。
回顧一下基本知識:
螢幕尺寸 是指螢幕對角線的長度
解析度 是指螢幕橫向畫素的數量 * 縱向畫素的數量。
在進行螢幕適配的時候需要注意幾個單位:
dp (裝置無關的畫素)在Android中是長度的單位
px (畫素)一個畫素表示螢幕上的一個點
dpi (畫素密度)每英寸內的畫素個數。
density (螢幕密度)
Android中的dp在渲染前會將dp轉為px,計算公式:
px = density * dp;
density = dpi / 160;
px = dp * (dpi / 160);
Android drawable
drawable-ldpi (dpi=120, density=0.75)
drawable-mdpi (dpi=160, density=1)
drawable-hdpi (dpi=240, density=1.5)
drawable-xhdpi (dpi=320, density=2)
drawable-xxhdpi (dpi=480, density=3)
直接看圖更直觀(網路截圖,非自畫)
這是一個勾股定理計算方式哈哈,比如一個手機解析度為1920 * 1080尺寸為5 那麼計算結果,畫素密度dpi就是 440。
在螢幕適配中遇到的問題是,比如UI小姐姐給我們切的圖是按照螢幕寬度360dp來設計的,經過計算手機螢幕實際上是
(1080 /( 440 / 160 )= 392.7dp),所以實際上螢幕寬度比設計圖要寬度要大。
常見的螢幕解析度適配有幾種吧:
1 在螢幕適配中使用dp而不使用px,dp可以保證在不同螢幕畫素密度的裝置顯示相同效果,所以在佈局使用dp作為單位。還有,在佈局設定的任何單位,在頁面渲染的時候,系統最終都會轉化為px。
缺點螢幕尺寸相差過大,無法保證相同。
2 切多套圖適配,針對不同機型使用不同的資原始檔,這樣會使UI設計人員工作量加大,同時造成資原始檔過多,apk體積變大。
3 .9圖片適配,.9圖片作為特殊的png圖片,可以不同機型進行適配。缺點是還要單獨設計製作.9圖片。
4 佈局適配,儘量使用wrap_content,match_parent以及權重weight,使用相對佈局,不要使用絕對佈局。缺點無法保證所有繪圖部分都是這樣的。
5 使用解析度適配,根據不同values下解析度進行適配,應用會自動尋找相適應的解析度。也叫寬高限定符如下,在資原始檔下生成不同的解析度資源,設計不同的尺寸,然後在佈局檔案引入對應的dimens,缺點是要收集市場上所有的機型解析度,比較繁瑣
6 程式碼適配,比如獲取LayoutParams的物件動態設定寬高,缺點需要計算,比較麻煩。
這裡給出幾位大佬的適配方案 拉丁吳大佬
鴻洋大神:
https://github.com/hongyangAndroid/AndroidAutoLayout
而今日頭條會根據公式算出density(也可以這樣理解density就是1dp佔用當前裝置多少畫素):
當前裝置螢幕總寬度(單位為畫素)/ 設計圖總寬度(單位為 dp) = density
今日頭條的做法就是強行將不同解析度的手機的寬度dp修改為統一的值。
螢幕的總 px 寬度 / density = 螢幕的總 dp 寬度
比如UI設計師給的設計圖的寬度是360dp,那麼對於任何不同解析度的手機裝置(不管是1080 * 720 或者是 1920 * 1080),開發同學總是根據公式算出
density(手機螢幕寬度畫素px / 360dp = density)然後動態修改不同解析度手機的density值,從而保證不同解析度手機的寬度dp具有一個統一的值比如360dp。
今日頭條的開發工程師真是厲害,玩的太溜了o( ̄▽ ̄)d
引入頭條:{ density 是 DisplayMetrics 中的成員變數,而 DisplayMetrics 例項通過 Resources#getDisplayMetrics 可以獲得,
而Resouces通過Activity或者Application的Context獲得。
因為佈局檔案最終會將dp轉化為px,實現方式是通過呼叫
TypedValue#applyDimension
(int unit, float value, DisplayMetrics metrics) 來進行轉換 }
看裡面的原始碼:
系統就是根據上面的方法,將專案中任何設定的單位,最終在渲染的時候都會轉化為px的。而常用的公式px = dp(dpi / 160) 就是根據上面的方法來的,
比如 density = dpi / 160 , px = value * density,而density是DisplayMetrics中的一個變數。
這個時候做法就是,如果保證螢幕的寬度和設計圖上的寬度保持一致的話,那我們在佈局時就可以直接按照設計圖上的尺寸填寫 dp 值就ok了。
今日頭條原理就是把螢幕的實際寬度轉換為設計圖上的寬度
今日頭條的適配方式只能以螢幕中高度或者寬度其中一個作為基準進行適配,為什麼不能同時進行適配呢?因為市場上Android機型太多了,基本上不同機型的螢幕寬高比都不一致,尤其現在各大廠商都主打 全面屏,問題更多了,以寬或者高其中一個作為基準進行適合配,可以有效避免佈局在寬高比不一致的螢幕上發生變形。
今日頭條的實現方式:
根據公式算出density值,然後將計算好的density值傳入下面的方法中,這個方法需要在Activity的onCreate方法中呼叫,然後修改系統的density值:
原理,實現方式總結完了,不由感嘆奇思妙想的今日頭條的工程師,並且有好幾位大佬都很力挺,比如 blanj大神,並且在今日頭條基礎上做了封裝和完善,感興趣的同學可以看一下
https://juejin.im/post/5b6250bee51d451918537021#heading-5
大佬們也分析的今日頭條適配方案的優缺點,這裡總結一下:
優點:
1 使用成本低,操作簡單,在頁面佈局的時候不需要額外的程式碼和操作。
2 這種方式和自己專案沒有耦合吧,並且使用的Android官方的API。
3 適配所有控制元件,只需要修改density值,專案中所有的控制元件都能使用。
4 比較穩定,不存在效能消耗。
缺點是對老專案不夠相容,系統的density值改變後,那麼整個佈局的實際尺寸都會改變,在老專案中,以前的佈局方式都要重新按照設計圖進行修改。
大佬們還推薦更完美的適配方案,那就是拉丁吳大神推薦的smallestWidth適配,感興趣的同學可以看一下
https://github.com/ladingwu/dimens_sw