小豬淺談Android螢幕適配
小豬淺談Android螢幕適配
tags: Tutorial
引言
國慶前在微信群裡看到有人在問Android螢幕適配的問題,湊巧自己最近時間
略有閒暇,索性來談談Android中螢幕適配相關的一些內容吧,鄙人才疏學淺,
所說的都是自己認知範圍以內的,不可能面面俱到,如有疏漏不妥之處,還請
不吝指出,謝謝!配上最近被玩壞的Gif~
Android中的單位與名詞
1.相對單位與絕對單位
對於計量單位,人們日常習慣性分為「相對單位」與「絕對單位」兩類,前者
根據不同的情景表現出不同的大小,比如Android裡的dp,px與sp等;而後者則是
制定了一個標準,cm(釐米),寫死了,就那麼多,1cm什麼情況下都是一樣大。
說到這個絕對單位,順帶提下兩個單位:
- in(英寸):1 in = 2.54cm
- pt(磅,印刷行業常用單位):1 pt = 1 / 72 in
2.px (pixel,畫素)
就是一個個的畫素點,影象的最小組成單元
3.dp (dip,密度無關畫素)
Density-independent pixel,抽象意義上的畫素,與裝置的實際物理畫素點無關,
可以保證在不同的畫素密度的裝置上顯示相同的效果,也是Android獨有的長度單位。
1dp表示在螢幕畫素密度為160dpi的螢幕上,1dp = 1px;
類推,在320dpi上,1dp = 2px,不難看出這樣的轉換公式:px = dp * (dpi / 160)
4.sp (sip,獨立比例畫素)
scale-independent pixel,字型大小專用單位,**會根據系統設定的字型大小進行
縮放,推薦使用**12sp以上的字型(12sp以下太小),不推薦用奇數和小數,容易造成
精度丟失問題。
Tip:
儘管官方建議我們字型都用sp作為單位,但是sp字型會根據系統設定的字型大小進行
縮放,假如使用者修改了系統字型大小,可能會導致我們APP的UI受到影響,比如字型
大了,然後各種顯示不全,個人還是建議使用dp來做為字型單位!
5.手機外觀尺寸
整個手機的尺寸,不止螢幕,一般在中關村或者其他評測網站都可以查到。
一般是用mm為單位,依次為長 x 寬 x 厚度,比如我的老掉牙的moto x 的尺寸就是:
159.3mm × 83mm × 10.1mm。
6.螢幕尺寸
螢幕對角線的長度,單位是英寸,計算公式如下:
其實就是勾股定理求對角線長度,比如長4寸,寬3寸的手機,他的螢幕
尺寸計算:
7.解析度(Resolution)
螢幕豎直方向與水平方向的畫素個數,比如:1280*720 就說明螢幕的
豎直方向上有1280個畫素點,而水平方向上有720個畫素點,單位px。
8.dpi與ppi
- dpi:dot per inch,點密度,每英寸多少個點,一般用作表示印刷品點密度;
- ppi:pixels per inch,畫素密度,每英寸所包含的畫素數目;
一般用作表示顯示裝置的點密度。
兩者的值近乎相等,不用過於糾結!這個值的計算公式如下:
9.density(螢幕密度)
這個單位感覺是用來表示不同dpi的倍數關係,計算公式:density = dpi/160
10.常用單位轉換工具類
public class DensityUtil {
public static int dp2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
public static int px2dp(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
}
11.獲取螢幕尺寸與密度的三種方法
public class GetScreenParameter {
//方法一:已過時,可使用,但不建議使用
public static void getResolution1(Context mContext) {
Display mDisplay = ((Activity) mContext).getWindowManager()
.getDefaultDisplay();
int W = mDisplay.getWidth();
int H = mDisplay.getHeight();
}
//方法二:通過getWindowManager來獲取螢幕尺寸的
public static void getResolution2(Context mContext) {
DisplayMetrics mDisplayMetrics = new DisplayMetrics();
((Activity) mContext).getWindowManager().getDefaultDisplay()
.getMetrics(mDisplayMetrics);
int W = mDisplayMetrics.widthPixels;
int H = mDisplayMetrics.heightPixels;
// 螢幕密度(0.75 / 1.0 / 1.5)
float density = mDisplayMetrics.density;
// 就是螢幕密度 * 160而已,螢幕密度DPI(120 / 160 / 240)
int densityDpi = mDisplayMetrics.densityDpi;
}
//方法三:通過getResources來獲取螢幕尺寸的,大部分用這個
public static void getResolution3(Context mContext) {
DisplayMetrics mDisplayMetrics = new DisplayMetrics();
mDisplayMetrics = mContext.getResources().getDisplayMetrics();
int W = mDisplayMetrics.widthPixels;
int H = mDisplayMetrics.heightPixels;
float density = mDisplayMetrics.density;
int densityDpi = mDisplayMetrics.densityDpi;
}
}
Tip(可以不看,現在基本不會用這麼小屏的手機了):
對於螢幕密度很低的小屏手機,比如240*320,計算出來的尺寸,可能為:320*427
原因是:沒有設定多解析度支援的話,Android系統會將240x320的低密度(120)尺寸
轉換為中等密度160dpi對應的尺寸,如果你想獲得正確的物理尺寸,需要在
AndroidManifest.xml裡新增下述程式碼:
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:resizeable="true"
android:anyDensity="true"/>
Android適配準備
1.什麼是Android適配
答:因為Android系統的開放性,很多廠商都喜歡對Android系統和硬體進行個性化定製,
以達到他們想要的樣子,這種結果帶來的「Android系統」和「手機螢幕」的碎片化問題。
只對市場佔用率較高的720和1080進行適配顯然是不夠的,為了讓我們的Android應用在各種
各樣的手機上保證介面效果一致,各種手機螢幕的適配顯得非常重要,也是每個開發者
為之頭痛的問題。
2.Android中的六種通用螢幕密度
螢幕密度 | 範圍(dpi) | 標準解析度 | dp與px | 圖示尺寸 |
---|---|---|---|---|
ldpi(QVGA) | ~ 120 | 240 * 320 | 1dp=0.75px | 36 * 36 |
mdpi(HVGA) | 120 ~ 160 | 320 * 480 | 1dp=1px | 48 * 48 |
hdpi(WVGA) | 160 ~ 240 | 480 * 800 | 1dp=1.5px | 72 * 72 |
xhdpi(720P) | 240 ~ 320 | 720 * 1280 | 1dp=2px | 96 * 96 |
xxhdpi(1080p) | 320 ~ 480 | 1080 * 1920 | 1dp=3px | 144 * 144 |
xxxhdpi(2K) | 480 ~ 640 | 1440 × 2560 | 1dp=4px | 192 * 192 |
Tip:圖示大小 = px數 * 4 * 12
3.關於UI設計稿的適配
我們可以通過友盟的 全域羅盤 知道當前國內的移動裝置使用情況,
以此瞭解需要適配的趨勢,以八月份的統計為例,擷取螢幕尺寸與解析度佔比
拍醒前五的資料如下:
更多詳細資訊可自行檢視,從上面兩個表格不難看出這樣的趨勢:
5.5寸 和 5寸 螢幕尺寸市場佔比最高,而解析度:1920x1080 和 1280x720
依舊是當前主流。
也就是說讓設計師按照720p和1080p出兩套設計稿就可以適配大部分的
裝置了,但是不同的公司因為人員或者時間等外部因素,可能有這幾
種情況:
- 三套:720p,1080p,1440p,這是最理想的情況;
- 兩套:720p,1080p,主流,大部分公司都或走兩套;
- 一套:720p,又或者和iOS共用一套 750x1334,可以當做720p的設計稿
來編寫UI,儘管比例和接近16:9,但是還是有些偏差,需要Android對UI進行微調!
UI能做的基本就到這裡了,至於其他解析度的手機則需要我們開通過
相關的手段來適配了。
Android適配開始
1.最簡單一些適配技巧
先來說幾個爛大街的適配技巧吧
1) 使用dp而非px
dp是畫素無關的,而在實際使用中1dp大約等於1/160 in,比如一個160dp * 160dp
的控制元件,在大多數的螢幕上都能保持1 in * 1 in 的大小。
但是,並不是能解決所有問題的,以下兩點要注意:
- 1.實際效果還是會有些差距的,僅僅是相近而已;
- 2.當裝置的物理尺寸存在差異的時候,dp就顯得無能為力了。
比如,為4.3寸螢幕準備的UI,執行在5.0寸的螢幕上,很可能在
右側和下側存在大量的空白;而5.0寸的UI執行到4.3寸的裝置上,很可能顯示不下。
2) 少寫固定尺寸
「 少寫固定尺寸,而使用 wrap_content, match_parent 與 weight 權重 」
3) 使用相對佈局,不要使用絕對佈局
常識,而且絕對佈局基本退出歷史舞臺了,可以忽略…
4) 自動拉伸的.9圖
常識,.9圖的作用是:拉伸的時候特定的區域不會發生圖片失真,而不失真的區域
可以由我們自己繪製,從而實現圖片適配。
5) 使用shape代替純色圖片
常識,一些純色的矩形,圓角,圓都可以通過編寫shape檔案來替換,比起png,
xml檔案小太多。
6)使用SVG向量圖替換點陣圖
可能有些朋友對SVG向量圖有些陌生,其實和普通的點陣圖最大的區別就是:
SVG是通過「XML檔案」來定義一個圖形,通過一些特定的語法和規則來繪製
出我們所需的影象,而不是點陣圖那樣通過儲存影象中每一點的畫素值來儲存
與使用圖形。
SVG是已經定義好怎麼畫這個圖,需要的時候再去畫,因為是按照特定的語法
和規則,理論上支援任何級別的縮放,而且不會失真,相比起多套同樣的點陣圖
檔案,方便太多。
向量圖雖好,但是有幾點要注意的:
- 1.適用於Android 5.0以上,儘管官方有相容包,低版本還是會有些問題的!
- 2.不適合細節過於複雜的圖片!
- 3.因為是用到的時候才畫,所以載入圖片所消耗的時間和資源可能會增加。
至於怎麼用這個向量圖,你可以讓美工在PS裡把圖片匯出為SVG/PSD格式,然後
AS裡右鍵drawable資料夾:
選中本地檔案,確定
然後AS會幫你自動生成一個向量xml檔案,比如我的一個tab圖示
右側可以看到預覽圖:
然後要用到圖片的地方直接引用即可。
至於SVG的規則以及如何自行編寫不在本節重點,有興趣的可自行搜尋用法~
2.多套資原始檔的套路
常用的套路依舊不能解決大部分的螢幕適配問題,Android給我們提供了
備用資源這個東西,詳細可見官網:提供備用資源
簡單說就是按照規範,建立螢幕對應的資原始檔夾和資原始檔,
Android會自動去載入對應資料夾裡的資源的值,可能表述得有些不清晰,
舉個簡單的例子:
比如我們xxhdpi,padding為3dp,而在xxxhdpi下的padding為5dp
我們就可以建立一個對應的values資料夾和dimens.xml檔案
相信你看到這裡就明瞭,官網寫的這個也就清晰了
而這個qualifier限定符怎麼拼接可以看官網,或者看下面的簡表:
看到這裡,你可能會有問題,我要寫多少套?
講真,我也不知道,只能說看需要,但是想跟你說限定符裡一個很常用的:smallestWidth
螢幕區域的最小尺寸,比如 values-sw320dp,只有在螢幕寬度不小於
320dp的才會使用這個資料夾裡的dimens.xml檔案,我們還可以另外建立一個
values-sw360dp,然後320dp那個資料夾只有在螢幕寬度在320dp到360dp
內才回去呼叫了,drawable,layout等資原始檔也是同樣的套路。
3.使用指令碼對長度按照不同解析度進行比例轉換
就是拿螢幕寬度,去除以360,得出比例去乘以對應的dp值
比如:sw720dp裡一個單位的1 dp = 2dp,而sw800dp裡的1 dp = 2.22dp:
Github地址:https://github.com/mengzhinan/PhoneScreenMatch
具體的自己看文件和程式碼吧,可能有的疑問就是不是win電腦執行不了bat檔案
的問題,比如mac,直接命令列,cd到 PhoneScreenMatch/app/src/main
目錄下,鍵入下述命令即可:
java -jar screenMatchPX.jar
專案中還提供了一個px適配的,分開寬高做等值縮放的,對於長寬比很奇葩
的機子可以試試。當然這種適配方式帶來的最明顯的問題就是資原始檔增加
的問題,這需要自行權衡。
類似的方案還有:https://github.com/paulyung541/EasyScreen
4.[過時] 百分比佈局庫支援庫
這個庫API 26.0.0 後的版本已經棄用了,在官方倉庫可以看到
[DEPRECATED in support lib v26] you should now use ConstraintLayout widget
如果你想使用百分比來編寫建議使用約束佈局 ConstraintLayout。
所以android-percent-support怎麼用並不講,想通過看看原始碼的方式
來了解百分比的實現套路,沒興趣的可以跳過了,或者看其他關於
百分比佈局支援庫的其他使用擴充套件文章:
原始碼過一過 (流程參照CSDN張鴻洋的博文)
lib庫裡的東西不多,就三個核心的類而已
如果讓我們來做套路可能是:
- 通過LayoutParams獲取佈局中設定的與百分比有關的屬性
- 父佈局拿到這些屬性值後進行動態計算,比如寬*寬佔百分比
- 子控制元件呼叫measure(計算後值)進行繪製。
看下程式碼具體是怎麼做的,首先是獲取Layout裡子控制元件的屬性,開啟
PercentFrameLayout,重寫了generateLayoutParams方法,
然後新建了一個LayoutParams物件
看看這個LayoutParams裡賣的什麼藥:
通過PercentLayoutHelper的getPercentLayoutInfo方法構造一個
PercentLayoutInfo物件,而getPercentLayoutInfo裡做的事則
是把attrs裡的東東都拿出來,然後塞到PercentLayoutInfo物件用於返回:
而這個PercentLayoutInfo裡定義了一堆百分比相關的屬性,
還有一些方法,字面上大概可以知道是設定LayoutParams相關
的方法,就是把onMeasure裡的邏輯操作抽取到了這裡。
好的,屬性拿到了,接著就是跟onMeasure方法了,呼叫了
PercentLayoutHelper物件裡的adjustChildren方法
方法裡做的東西也比較簡單,拿到佈局的寬高,然後
迴圈遍歷佈局裡的子控制元件,如果params是百分比型別的
取出PercentLayoutInfo裡info,然後去設定寬,高和margin。
再跟下fillLayoutParams方法,儲存原本寬高後(後面用來重置),重新設定寬高,
fillMarginLayoutParams也是類似,百分比的基本套路就已經完了。
不過專案中還多了兩個細節,計算值過小的情況以及重置原佈局尺寸
onMeasure最後還呼叫handleMeasuredStateTooSmall方法,而方法做的
事是出去容器中所有的子控制元件,如果params是百分比型別的,則呼叫
shouldHandleMeasuredWidthTooSmall方法判斷值是不是太小,太小則
把寬或高設定為ViewGroup.LayoutParams.WRAP_CONTENT(圖中 = -2那裡)
needsSecondMeasure設定為true,代表需要二次重繪。
而重置佈局尺寸的程式碼則是寫在onLayout方法裡(onMeasure後會回撥)
一樣是遍歷子控制元件,然後把引數都重置為之前存mPreservedParams裡的值,
而這個值是佈局裡設定的0dp。(感覺沒什麼用處…)
因為onMeasure的關鍵程式碼都寫到Helper類裡,擴充套件一個百分比的線性
佈局也很簡單,拷貝下PercentFrameLayout改點東西就好。
另外,因為依賴於父容器,導致ScrollView,ListView等容器內高度無法使用百分比。
而鴻洋還另外對官方的百分比進行了擴充套件,寫了一個android-percent-support-extend,
有興趣可以移步到他的部落格閱讀:Android 增強版百分比佈局庫 為了適配而擴充套件
5.[作者停止維護] AndroidAutoLayout
又是鴻洋大神的庫,不過作者已經不維護了,使用之前還是三思!!!
部落格連結:http://blog.csdn.net/lmj623565791/article/details/49990941
大概套路是:
定好設計稿尺寸,直接選擇對應解析度的預覽,AndroidAutoLayout 庫會根據
佔屏比例,自動計算轉換當前螢幕下適配的大小。載入佈局其實是把各自的
Layout都轉換為對應的AutoLayout,從而不用在所有的xml中進行更改。
思路很gay,但是也有一些弊端,在onMeasure的時候進行數值計算,存在效能問題;
擴充套件性差,用到的ViewGroup都需要自行對AutoLayout進行擴充套件。
最重要的是:
issues較多,而且作者不維護,應用到實際專案中還需自行權衡…
6.ConstraintLayout 約束佈局
使用約束的方式來指定各個控制元件的位置和關係的,可以看做更加強大的
RelativeLayout,通過圖形化介面拖拽的方式來編寫介面,當然也可以
直接用XML進行編寫,畢竟拖拽的背後也是XML來實現的,不過屬性比較
多,直接編寫顯得有些繁瑣,建議還是拖拽後自己在手動修改引數。
ConstraintLayout約束佈局除了支援百分比外,相比傳統佈局一層
套一層的巢狀佈局結構,編寫的XML元素層次結構更簡單(一個外層
ConstraintLayout包全部),而且效能更優。
至於問題的話,可能就是佈局中的元素過於複雜時,拖拽可能會有少許問題;
其他的話暫時沒發現,關於ConstraintLayout與傳統佈局方式的效能對比可見:
解析ConstraintLayout的效能優勢
而關於圖形化拖拽的,郭霖大神有篇詳細講解的博文:
Android新特性介紹,ConstraintLayout完全解析
而關於屬性講解和具體例項的,可以翻看鴻洋的博文:
ConstraintLayout 完全解析 快來優化你的佈局吧
附:記下關鍵點,方便自己日後回憶
- Inspector:右側Properties區域的上半部分,豎直和水平的軸用於確定
位置;圖中的四個16確定的是間距;中間的四個 >>> 的箭頭確定的是大小
有三種可選的模式: wrap_content、 固定值、 any size
用於填充滿當前控制元件的約束規則,和match_parent是不同的!
- Guidelines:參照線 ,就是弄個線給別的控制元件提供約束,有垂直和水平
兩種,用來實現一些百分比的效果很贊,比如下圖這種兩個控制元件並排居中的佈局:
- 自動新增約束:拖拽控制元件後,一個個去新增約束非常繁瑣,ConstraintLayout中
支援自動新增約束,有兩種自動新增約束的方式:
AutoConnect:預設關閉,啟用後拖拉控制元件到佈局裡會自動生成約束
Infer Constraints:控制元件拖拉得差不多後,點選後會為所有控制元件生成約束
當然,這兩種自動生成的約束可能有些問題,我們可以自己另外調整下~
7.等比例縮放的另一種實現方案
同樣是等比例縮放的套路,原文可見:一種粗暴快速的Android全螢幕適配方案
上面也說了AndroidAutoLayout在onMeasure中進行數值運算可能會有效能問題。
而這個方案則是規避了這個問題,直接在Android進行長度計算的時候就進行換算;
系統長度計算的入口是TypedValue裡的applyDimension方法:
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;
}
}
而作者選擇比較冷門的pt單位,從而避免對正常的px,dp,sp造成影響,
PT的計算為:value * metrics.xdpi * (1.0f/72),我們可以動前面
的metrics.xdpi,這個值應改為:縮放比例*72,*72的原因是想轉px,
而metrics可以通過context.getResources().getDisplayMetrics()
拿到。作者另外寫了一個Helper類,通過構造方法傳入設計稿的寬度除以
當前裝置的解析度得出比例,然後乘以72即可得出對應解析度下的尺寸。
呼叫也很簡單,直接在Application類啟用即可:
new RudenessScreenHelper(this, 750).activate();
等比縮放套路換湯不換藥,不過很機智的規避了複雜的計算問題,想法是挺讚的,
有興趣的可以試試。
小結
在本節中,筆者對於自己所知的Android原生適配套路都一一進行了複述,相信會對
你在開發中的螢幕適配有所幫助,當然具體要怎麼適配還是看需求吧,比如我在的
小作坊,也是隻是適配720p,2333;當然有其他的套路也歡迎告知,萬分感激~
(PS:說來慚愧,本來想著國慶能發的,後面因為各種瑣事拖到今天…)
相關文章
- 淺談-web螢幕適配的解決方案Web
- android 螢幕適配Android
- Android技能樹 — 螢幕適配小結Android
- Android 主流螢幕以及適配Android
- Android 螢幕適配工具類Android
- android螢幕適配詳解Android
- Android 螢幕適配終結者Android
- Android開發之螢幕適配Android
- android螢幕適配的問題Android
- Android 螢幕適配最佳實踐Android
- Flutter螢幕適配Flutter
- UIWebView 適配螢幕UIWebView
- android螢幕適配三:通過畫素密度適配Android
- Android螢幕適配(理論適配100%機型)Android
- Android螢幕適配總結和思考Android
- flutter 螢幕尺寸適配 字型大小適配Flutter
- Android螢幕適配前先了解這些Android
- android 今日頭條的螢幕適配理解Android
- 安卓螢幕適配的方案安卓
- Android 螢幕適配:最全面的解決方案Android
- android 螢幕適配二:手寫百分比佈局適配Android
- android 螢幕適配一:通過自定義View的方式實現適配AndroidView
- Flutter螢幕適配 - 等比縮放Flutter
- 【postcss-px-to-viewport】螢幕適配CSSView
- Android最全螢幕適配的幾個重要概念(三)Android
- 移動 web 端螢幕適配 – remWebREM
- H5 分層螢幕適配H5
- 移動APP測試-Android螢幕適配問題(一)APPAndroid
- 移動APP測試:Android螢幕適配問題二APPAndroid
- Android螢幕適配dp、px兩套解決辦法Android
- 【移動適配】移動Web怎麼做螢幕適配(一)Web
- 【移動適配】移動Web怎麼做螢幕適配(三)Web
- 淺談移動前端適配前端
- android螢幕適配方法Android
- Android螢幕適配方案Android
- Android 螢幕適配方案Android
- Android 螢幕自適應Android
- 極其簡單的Flutter 螢幕適配Flutter