Android屏幕適配:你需要了解的所有適配方法

引言 ? ?

????一直很想寫文章,無奈自己才疏學淺,遲遲沒有想寫的沖動。而且相比較之下,大神們寫的博客已經很好了,認為自己沒必要再花費精力去寫文章。話雖如此,但隨著學習時間的推移,發現自己有時候記憶會產生碎片化。因此還是決定通過寫文章來縷清思路,也為自己的學習留下筆記,畢竟好記性不如爛筆頭。

1 為什么要進行Android屏幕適配?

官網解釋:

????Android 可在各種具有不同屏幕尺寸和密度的設備上運行。對于應用,Android 系統在不同設備中提供一致的開發環境,可以處理大多數工作,將每個應用的用戶界面調整為適應其顯示的屏幕。 同時,系統提供 API,可用于控制應用適用于特定屏幕尺寸和密度的 UI,以針對不同屏幕配置優化 UI 設計。 例如,您可能想要不同于手機 UI 的平板電腦 UI。

????雖然系統為使您的應用適用于不同的屏幕,會進行縮放和大小調整,但您應針對不同的屏幕尺寸和密度優化應用。 這樣可以最大程度優化所有設備上的用戶體驗,用戶會認為您的應用實際上是專為他們的設備而設計,而不是簡單地拉伸以適應其設備屏幕。

? ? ? ? ?通俗的講就是Android系統手機屏幕碎片化嚴重,為了提高用戶體驗,需要盡可能的對所有產商的手機屏幕進行適配,讓用戶在感官上和使用上都有好的體驗。

2 術語和概念

2.1 屏幕尺寸(size)

? ? 按屏幕對角測量的實際物理尺寸。單位:英寸,1英寸 = 2.54cm。

圖2-1 屏幕對角線

? ? Android 將所有實際屏幕尺寸分組為四種通用尺寸:小(small)、 正常(normal)、大(large)和超大(xlarge)。

圖2-2 真實尺寸對應通用尺寸

? ? ? ? ?2018年市面上常見的手機屏幕尺寸有:5.0、5.2、5.5、6寸等等,它們所對應的通用尺寸為large。

2.2 屏幕密度dpi和ppi

????2.2.1 dpi

????屏幕物理區域中的像素量,通常稱為 dpi(dots per inch,每英寸點數)。

? ? Android 將所有屏幕密度分組為六種通用密度: 低(ldpi)、中(mdpi)、高(hdpi)、超高(xhdpi)、超超高(xxhdpi)和超超超高(xxxhdpi)。

? ? ? ? ?一般情況下,240×320的屏幕是低密度120dpi,即ldpi;320×480的屏幕是中密度160dpi,即mdpi;480×800的屏幕是高密度240dpi,即hdpi;720×1280的屏幕是超高密度320dpi,即xhdpi;1080×1920的屏幕是超超高密度480dpi,即xxhdpi。

? ? 2.2.2 ppi

? ?屏幕實際區域中的像素量,每英寸所擁有的像素點的數量ppi(pixels per inch),ppi數值越高顯示越細膩。

? ?ppi可以通過圖2-3公式計算。

圖2-3 ppi計算公式

? ? ? ? ?如圖2-4,通過公式計算得出:sqrt(1080*1080+1920*1920) / 5 = 440ppi。

圖2-4 ppi計算


2.3 方向

????從用戶視角看屏幕的方向,即橫屏或豎屏,分別表示屏幕的縱橫比是寬還是高。請注意, 不僅不同的設備默認以不同的方向操作,而且方向在運行時可隨著用戶旋轉設備而改變。

2.4 分辨率

????屏幕上物理像素(px)的總數。

? ? 例如:1080*1920px的屏幕,屏幕橫向有1080個像素,屏幕縱向有1920個像素。像素總數為?1080*1920 =?2073600?個像素。

2.5 密度無關像素 (dp)

????在定義UI布局時應使用的虛擬像素單位,用于以密度無關方式表示布局維度或位置。

????密度無關像素等于 160 dpi 屏幕上的一個物理像素,這是 系統為“中”密度屏幕假設的基線密度。在運行時,系統根據使用中屏幕的實際密度按需要以透明方式處理 dp 單位的任何縮放 。

????dp 單位轉換為屏幕像素很簡單:px = dp * (dpi / 160)。

????例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定義應用的 UI 時應始終使用 dp 單位 ,以確保在不同密度的屏幕上正常顯示 UI。

2.6 sp

? ? 和dp類似,在定義字體大小時使用sp,字體可根據屏幕進行縮放。

3 系統如何顯示最佳效果

1. 系統使用適當的備用資源

????根據當前屏幕的尺寸和密度,系統將使用您的應用中提供的任何尺寸和密度特定資源。例如,如果設備有高密度屏幕,并且應用請求可繪制對象資源,系統將查找與設備配置最匹配的可繪制對象資源目錄。根據可用的其他備用資源,包含?hdpi?限定符(例如?drawable-hdpi/)的資源目錄可能是最佳匹配項,因此系統將使用此目錄中的可繪制對象資源。

2. 向上或向下擴展資源

????如果沒有匹配的資源,系統將使用默認資源,并按需要向上或向下擴展,以匹配當前的屏幕尺寸和密度。

????“默認”資源是指未標記配置限定符的資源。例如,drawable/?中的資源是默認可繪制資源。 系統假設默認資源設計用于基線屏幕尺寸和密度,即 正常屏幕尺寸和中密度。 因此,系統對于高密度屏幕向上擴展默認密度資源,對于低密度屏幕向下擴展。

????當系統查找密度特定的資源但在密度特定目錄中未找到時,不一定會使用默認資源。系統在縮放時可能改用其他密度特定資源提供更好的效果。例如,查找低密度資源但該資源不可用時, 系統會縮小資源的高密度版本,因為系統可輕松以 0.5 為系數將高密度資源縮小至低密度資源,與以 0.75 為系數 縮小中密度資源相比,偽影更少。

????如需有關 Android 如何通過使配置限定符與設備配置匹配來選擇備用資源的更多信息,請參閱Android 如何查找最佳匹配資源

4 如何支持多種屏幕

1.在 XML 布局文件中指定尺寸時使用?wrap_content、match_parent 或dp。

????為 XML 布局文件中的視圖定義 android:layout_width?和 android:layout_height 時,使用?"wrap_content"、"match_parent"?或?dp?單位可確保在當前設備屏幕上為 視圖提供適當的尺寸。

????例如,layout_width="100dp"?的視圖在 中密度屏幕上測出寬度為 100 像素,在高密度屏幕上系統會將其擴展至 150 像素寬, 因此視圖在屏幕上占用的物理空間大約相同。

????類似地,您應選擇?sp(縮放獨立的像素)來定義文本大小。sp?縮放系數取決于用戶設置,系統會像處理?dp?一樣縮放大小。

2.不要在應用代碼中使用硬編碼的像素值(px)

? ??由于性能的原因和簡化代碼的需要,Android 系統使用像素作為表示尺寸或坐標值的標準單位。這意味著, 視圖的尺寸在代碼中始終以像素表示,但始終基于當前的屏幕密度。 例如,如果?myView.getWidth()?返回 10,則表示視圖在 當前屏幕上為 10 像素寬,但在更高密度的屏幕上,返回的值可能是 15。如果在應用代碼中使用像素值來處理預先未針對當前屏幕密度縮放的位圖,您可能需要縮放代碼中使用的像素值,以與未縮放的位圖來源匹配。

3.在 XML 布局文件中指定時使用weight。

? ?weight是權重屬性,通過設置它可以改變控件在布局中的權重。 Google官方推薦,當使用weight屬性時,將android:layout_width設為0dp,這樣就可以理解為自動占比。

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

? ? android:layout_width="match_parent"

? ? android:layout_height="match_parent"

? ? android:orientation="horizontal">

<TextView

? ? ? ? android:id="@+id/textView"

? ? ? ? android:layout_width="0dp"

? ? ? ? android:layout_height="wrap_content"

? ? ? ? android:layout_weight="1"

? ? ? ? android:text="TextView" />

</TextView>

<TextView

? ? ? ? android:id="@+id/textView2"

? ? ? ? android:layout_width="0dp"

? ? ? ? android:layout_height="wrap_content"

? ? ? ? android:layout_weight="2"

? ? ? ? android:text="TextView" />

</TextView>

</LinearLayout>

圖4-1 在布局中使用weight

? ? ? ? 在ConstraintLayout中,spread?和?spread inside?Chain 鏈可以設置每個組件的 weight 權重,這跟?LinearLayout?的?weight?權重設置很像,它可以設置為小數。

4.為不同屏幕密度提供替代位圖可繪制對象(備用資源)

???如果需要精確控制應用在不同 屏幕配置上的外觀,請在配置特定的資源目錄中調整您的布局和位圖可繪制對象。

????例如,考慮要顯示在中密度和高密度屏幕上的圖標。只需創建兩種不同大小的圖標 (例如中密度使用 100x100,高密度使用 150x150),然后使用適當的限定符以適當的方向放置兩個變體:

res/drawable-mdpi/icon.png???//for medium-density screens

res/drawable-hdpi/icon.png???//for high-density screens

? ? 也有的情況是會在根據sw最小寬度限定符去寫很多的dimens,例如一種非常好用的Android屏幕適配

????但是dimens多了會影響啟動速度,因此dimens的使用是根據實際需求來寫,并不是越詳細越好。這是我反編譯一個軟件得到的,僅供參考:


圖4-2 values下都有dimens

????常見的限定符有尺寸限定符最小寬度限定符,屏幕方向限定符。例如,以下應用資源目錄為不同屏幕尺寸對象提供不同的布局設計。

res/layout/my_layout.xml? ? ? ? ? ? ? // layout for normal screen size ("default")

res/layout-large/my_layout.xml? ? ? ? // layout for large screen size

res/layout-xlarge/my_layout.xml? ? ? // layout for extra-large screen size

res/layout-xlarge-land/my_layout.xml? // layout for extra-large in landscape orientation

? ? 如需了解如何使用備用資源的更多信息以及配置限定符的完整列表(不只是屏幕配置),請參閱提供備用資源

? ? 我們還可以根據需要使用配置限定符,常見的配置限定符如圖3-2所示:

圖4-3 常見的配置限定符

? ? 總結為兩點:1.使用備用資源

? ? ? ? ? ? ? ? ? ? ? ? ? 2.使用配置限定符

? ??5. 使用.9圖

? ??如果您在可能改變尺寸的組件上使用簡單圖像,您很快會發現效果有些差強人意,因為運行組件會均勻地拉伸或縮小您的圖像。 解決方案是使用.9圖,這種特殊格式的 PNG 文件會指示哪些區域可以拉伸,哪些區域不可以拉伸。

5. 常用的工具類

5.1 單位轉換

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);

? ? }

}

5.2 獲取屏幕尺寸和密度

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;?

? ? ? ? float density = mDisplayMetrics.density; //獲取屏幕密度

? ? ? ? int densityDpi = mDisplayMetrics.densityDpi;//獲取dpi

? ? }?

? ? //方法三:通過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;

? ? }?

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375