<適配>Android應(yīng)用如何支持多種屏幕

我們?cè)陧?xiàng)目中,處理頁(yè)面布局和繪制的時(shí)候,常常會(huì)考慮如何更好的做到多種機(jī)型和屏幕的最大程度的支持灶似,亦即屏幕適配的問(wèn)題。我為何以“Android應(yīng)用如何支持多屏幕”為題瑞你,而非用“Android應(yīng)用如何做屏幕適配”為題呢酪惭?這是因?yàn)樵诠俜轿臋n中,便是以“支持多種屏幕”為題來(lái)講解的者甲,為了統(tǒng)一吧春感,固以此為題。

一虏缸、支持多種屏幕的原因

1鲫懒、Android 可在各種具有不同屏幕尺寸和密度的設(shè)備上運(yùn)行。
2刽辙、基于國(guó)情和現(xiàn)狀窥岩,各種屏幕尺寸和密度的設(shè)備機(jī)型各不相同。
3宰缤、對(duì)于應(yīng)用颂翼,Android 系統(tǒng)在不同設(shè)備中提供一致的開(kāi)發(fā)環(huán)境,可以處理大多數(shù)工作慨灭,將每個(gè)應(yīng)用的用戶界面調(diào)整為適應(yīng)其顯示的屏幕朦乏。
4、為了提高開(kāi)發(fā)效率氧骤,避免重復(fù)開(kāi)發(fā)項(xiàng)目呻疹。

二、術(shù)語(yǔ)和概念

1筹陵、屏幕尺寸:
按屏幕對(duì)角測(cè)量的實(shí)際物理尺寸刽锤。

為簡(jiǎn)便起見(jiàn)镊尺,Android 將所有實(shí)際屏幕尺寸分組為四種通用尺寸:小、正常姑蓝、大和超大鹅心。

2吕粗、屏幕密度:
屏幕物理區(qū)域中的像素量纺荧;通常稱為 dpi(每英寸 點(diǎn)數(shù))。例如颅筋,與“正持嫦荆”或“高”密度屏幕相比,“低”密度屏幕在給定物理區(qū)域的像素較少议泵。

為簡(jiǎn)便起見(jiàn)占贫,Android 將所有屏幕密度分組為六種通用密度: 低、中先口、高型奥、超高、超超高和超超超高碉京。

3厢汹、方向:
從用戶視角看屏幕的方向,即橫屏還是豎屏谐宙,分別表示屏幕的縱橫比是寬還是高烫葬。

請(qǐng)注意,不僅不同的設(shè)備默認(rèn)以不同的方向操作凡蜻,而且方向在運(yùn)行時(shí)可隨著用戶旋轉(zhuǎn)設(shè)備而改變搭综。

4、分辨率:
屏幕上物理像素的總數(shù)划栓。添加對(duì)多種屏幕的支持時(shí)兑巾, 應(yīng)用不會(huì)直接使用分辨率;而只應(yīng)關(guān)注通用尺寸和密度組指定的屏幕 尺寸及密度忠荞。

5蒋歌、密度無(wú)關(guān)像素 (dp):
在定義 UI 布局時(shí)應(yīng)使用的虛擬像素單位,用于以密度無(wú)關(guān)方式表示布局維度 或位置钻洒。

密度無(wú)關(guān)像素等于 160 dpi 屏幕上的一個(gè)物理像素奋姿,這是系統(tǒng)為“中”密度屏幕假設(shè)的基線密度。在運(yùn)行時(shí)素标,系統(tǒng)根據(jù)使用中屏幕的實(shí)際密度按需要以透明方式處理 dp 單位的任何縮放 称诗。dp 單位轉(zhuǎn)換為屏幕像素很簡(jiǎn)單: px = dp * (dpi / 160)。 例如头遭,在 240 dpi 屏幕上寓免,1 dp 等于 1.5 物理像素癣诱。在定義應(yīng)用的 UI 時(shí)應(yīng)始終使用 dp 單位 ,以確保在不同密度的屏幕上正常顯示 UI袜香。

三撕予、支持的屏幕范圍

從 Android 1.6(API 級(jí)別 4)開(kāi)始,Android 支持多種屏幕尺寸和密度蜈首,反映設(shè)備可能具有的多種不同屏幕配置实抡。 您可以使用 Android 系統(tǒng)的功能優(yōu)化應(yīng)用在各種屏幕配置下的用戶界面 ,確保應(yīng)用不僅正常渲染欢策,而且在每個(gè)屏幕上提供 最佳的用戶體驗(yàn)吆寨。

具體內(nèi)容見(jiàn)下表:

屏幕配置

四、如何支持多種屏幕

支持多種屏幕的目標(biāo)是創(chuàng)建一款在 Android 系統(tǒng)支持的通用屏幕尺寸上都可以 正常運(yùn)行且顯示良好的應(yīng)用踩寇。

概括:

1啄清、在 XML 布局文件中指定尺寸時(shí)使用 wrap_content、match_parent 或 dp 單位 俺孙。

2辣卒、不要在應(yīng)用代碼中使用硬編碼的像素值

3、不要使用 AbsoluteLayout(已棄用)

4睛榄、為不同屏幕密度提供替代位圖可繪制對(duì)象

詳解:

1荣茫、對(duì)布局尺寸使用 wrap_content、match_parent 或 dp 單位
為 XML 布局文件中的視圖定義 android:layout_widthandroid:layout_height 時(shí)懈费,使用"wrap_content"计露、"match_parent" 或 dp單位可確保在當(dāng)前設(shè)備屏幕上為視圖提供適當(dāng)?shù)某叽纭?/p>

例如,layout_width="100dp"
的視圖在中密度屏幕上測(cè)出寬度為 100 像素憎乙,在高密度屏幕上系統(tǒng)會(huì)將其擴(kuò)展至 150 像素寬票罐, 因此視圖在屏幕上占用的物理空間大約相同。

類(lèi)似地泞边,您應(yīng)選擇 sp(縮放獨(dú)立的像素)來(lái)定義文本 大小该押。sp 縮放系數(shù)取決于用戶設(shè)置,系統(tǒng)會(huì)像處理 dp
一樣縮放大小阵谚。

2蚕礼、不要在應(yīng)用代碼中使用硬編碼的像素值
由于性能的原因和簡(jiǎn)化代碼的需要,Android 系統(tǒng)使用像素作為 表示尺寸或坐標(biāo)值的標(biāo)準(zhǔn)單位梢什。這意味著奠蹬,視圖的尺寸在代碼中始終以像素表示,但始終基于當(dāng)前的屏幕密度嗡午。

例如囤躁,如果 myView.getWidth()
返回 10,則表示視圖在 當(dāng)前屏幕上為 10 像素寬,但在更高密度的屏幕上狸演,返回的值可能是 15言蛇。如果 在應(yīng)用代碼中使用像素值來(lái)處理預(yù)先未針對(duì) 當(dāng)前屏幕密度縮放的位圖,您可能需要縮放代碼中使用的像素值宵距,以與未縮放的位圖來(lái)源匹配腊尚。

如果應(yīng)用在運(yùn)行時(shí)操作位圖或處理像素值,請(qǐng)參閱有關(guān)其他密度注意事項(xiàng)的一節(jié)

3满哪、不要使用 AbsoluteLayout
與其他布局小工具不同婿斥,AbsoluteLayout會(huì)強(qiáng)制 使用固定位置放置其子視圖,很容易導(dǎo)致 在不同顯示屏上顯示效果不好的用戶界面翩瓜。因此受扳,AbsoluteLayout在 Android 1.5(API 級(jí)別 3)上便已棄用。

您應(yīng)改用 RelativeLayout兔跌,它會(huì)使用相對(duì)定位來(lái)放置其子視圖。例如峡蟋,您可以指定按鈕小部件顯示在文本小工具的“右邊”坟桅。

4、使用尺寸和密度特定資源
雖然系統(tǒng)會(huì)根據(jù)當(dāng)前屏幕配置擴(kuò)展布局蕊蝗,但您在不同的屏幕尺寸上可能要調(diào)整 UI仅乓,以及提供針對(duì)不同密度優(yōu)化的可繪制對(duì)象。

如果需要精確控制應(yīng)用在不同 屏幕配置上的外觀蓬戚,請(qǐng)?jiān)谂渲锰囟ǖ?資源目錄中調(diào)整您的布局和位圖可繪制對(duì)象夸楣。例如,考慮要顯示在中密度和高密度屏幕上的圖標(biāo)子漩。只需創(chuàng)建兩種不同大小的圖標(biāo) (例如中密度使用 100x100豫喧,高密度使用 150x150),然后使用適當(dāng)?shù)南薅ǚ?以適當(dāng)?shù)姆较蚍胖脙蓚€(gè)變體:

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

如需了解有效配置限定符的更多信息幢泼,請(qǐng)參閱使用配置限定符紧显。

額外的

5、在清單中顯式聲明您的應(yīng)用支持哪些屏幕尺寸
通過(guò)聲明您的應(yīng)用支持哪些屏幕尺寸缕棵,可確保只有 其屏幕受支持的設(shè)備才能下載您的應(yīng)用孵班。聲明對(duì)不同屏幕尺寸的支持也可影響系統(tǒng)如何在較大 屏幕上繪制您的應(yīng)用 — 特別是,您的應(yīng)用是否在屏幕兼容模式中運(yùn)行招驴。

要聲明應(yīng)用支持的屏幕尺寸篙程,應(yīng)在清單文件中包含 <supports-screens> 元素。

6别厘、使用可繪制對(duì)象
默認(rèn)情況下虱饿,Android 會(huì)縮放位圖可繪制對(duì)象(.png、.jpg 和 .gif 文件)和九宮格可繪制對(duì)象(.9.png 文件),使它們以適當(dāng)?shù)奈锢沓叽顼@示在每部設(shè)備上郭厌。

一些通用的按鈕和簡(jiǎn)單的圖標(biāo)袋倔,可以通過(guò)九宮格可繪制對(duì)象(.9.png 文件)或者通過(guò)xml(使用shape等)進(jìn)行繪制,則可在不同設(shè)備上進(jìn)行適當(dāng)?shù)目s放折柠,已達(dá)到無(wú)損效果宾娜。

注:您只需要為位圖文件(.png、.jpg 或 .gif)和九宮格文件 (.9.png) 提供密度特定的可繪制對(duì)象扇售。如果您使用 XML 文件定義形狀前塔、顏色或其他可繪制對(duì)象資源,應(yīng)該將一個(gè)副本放在默認(rèn)可繪制對(duì)象目錄中 (drawable/)承冰。

五华弓、屏幕常用適配總結(jié):

UI適配 — 常用方式:

  • 1、單位適配:
  • 在XML布局中盡量使用wrap_content困乒、match_parent 或 dp 單位 寂屏。
  • 在代碼中不要使用硬編碼的像素值。
  • 2娜搂、位圖資源適配:
  • 提供不同屏幕密度的圖片資源迁霎,若考慮包體積,一般選用drawable-xxxhdpi
  • 使用 .9 的圖片自適應(yīng)
  • svg矢量圖(可縮放)
  • 3百宇、布局控件組件適配:
  • 盡量使用約束布局ConstraintLayout
  • 使用 Fragment 將界面組件模塊化考廉,可復(fù)用
  • 4、布局資源配置適配:即 創(chuàng)建備用布局
  • 創(chuàng)建特定于屏幕尺寸的布局
  • 使用最小寬度限定符携御,即sw限定符適配昌粤,系統(tǒng)識(shí)別屏幕可用高度和寬度的最小尺寸的dp值(其實(shí)就是手機(jī)的寬度值)
  • 使用可用寬/高度限定符,如創(chuàng)建目錄:layout-h600dp
  • 添加屏幕方向限定符:提供橫向(layout-land)或縱向(layout-port)的布局目錄
  • 5啄刹、系統(tǒng)屬性配置適配:
  • 使用預(yù)縮放的配置值:
    使用 ViewConfiguration 類(lèi)來(lái)獲取 Android 系統(tǒng)常用的距離涮坐、速度和時(shí)間。
    例如鸵膏,可通過(guò) getScaledTouchSlop() 來(lái)獲取框架用作滾動(dòng)閾值的距離(以像素為單位)
  • 聲明最大屏幕尺寸:<supports-screens>
    Android 可自動(dòng)調(diào)整應(yīng)用以適應(yīng)大屏幕的方式膊升。
    若有特殊需求,可通過(guò)在 <supports-screens> 清單標(biāo)記中指定largestWidthLimitDp 屬性
  • 限制應(yīng)用僅支持平板電腦或電視:使用 <supports-screens> 清單元素來(lái)阻止手機(jī)設(shè)備下載您的應(yīng)用谭企。
  • 限制應(yīng)用僅支持特定尺寸和密度:使用 <compatible-screens> 清單元素來(lái)指定應(yīng)用支持的確切屏幕尺寸和密度廓译。【不推薦】

參考鏈接:
官方:設(shè)備兼容性

UI適配 — 修改density值

  • density值:
    代表 1dp 占當(dāng)前設(shè)備多少像素债查,即 density = dpi / 160
px = density * dp;
density = dpi / 160;
px = dp * (dpi / 160);
  • ppi像素密度
    指每英寸包含的物理像素的數(shù)量非区。
    ppi 是設(shè)備在物理上的屬性值,取決于屏幕自身盹廷,計(jì)算公式如下所示征绸。被除數(shù)和除數(shù)都屬于客觀不可改變的值,所以 ppi 也是 無(wú)法修改 的,是硬件上一個(gè)客觀存在無(wú)法改變的值
    ppi
  • dpi屏幕像素密度
    軟件概念 上單位距離對(duì)應(yīng)的像素總數(shù)管怠。
    是手機(jī)在出廠時(shí)就會(huì)被寫(xiě)入系統(tǒng)配置文件中的一個(gè)屬性值淆衷,一般情況下 用戶是無(wú)法修改 該值的,但在開(kāi)發(fā)者模式中有修改該值的入口渤弛,是 軟件上可修改 的一個(gè)值祝拯。

  • 說(shuō)明:
    Android系統(tǒng)渲染UI前,最終都是通過(guò)轉(zhuǎn)換成px來(lái)計(jì)算的她肯,通過(guò)調(diào)用 TypedValueapplyDimension 方法佳头。系統(tǒng)的轉(zhuǎn)換代碼如下:

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

圖片中也會(huì)用到 density

   @Nullable
   public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
           @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
       validate(opts);
       if (opts == null) {
           opts = new Options();
       }

       if (opts.inDensity == 0 && value != null) {
           final int density = value.density;
           if (density == TypedValue.DENSITY_DEFAULT) {
               opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
           } else if (density != TypedValue.DENSITY_NONE) {
               opts.inDensity = density;
           }
       }
       
       // 此處用到了densityDpi
       if (opts.inTargetDensity == 0 && res != null) {
           opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
       }
       
       return decodeStream(is, pad, opts);
   }

  • 核心原理:
    即今日頭條適配方案,該方案的核心在于:將不同尺寸分辨率手機(jī)的寬度dp值改成一個(gè)統(tǒng)一的值晴氨,從而解決屏幕適配的問(wèn)題康嘉。

density = 當(dāng)前設(shè)備屏幕總寬度(單位為像素)/ 設(shè)計(jì)圖總寬度(單位為 dp)

   private static final float defaultWidth = 360;
   private static float appDensity;
   private static float appScaleDensity;

   public static void setCustomDensity(Application application, Activity activity){
       DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
       if (appDensity == 0){
           appDensity = displayMetrics.density;
           appScaleDensity = displayMetrics.scaledDensity;
           //設(shè)置修改系統(tǒng)字體以后的監(jiān)聽(tīng)
           application.registerComponentCallbacks(new ComponentCallbacks() {
               @Override
               public void onConfigurationChanged(@NonNull Configuration newConfig) {
                   if (newConfig != null && newConfig.fontScale >0){
                       appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
                   }
               }

               @Override
               public void onLowMemory() {

               }
           });
       }
       final float targetDensity = displayMetrics.widthPixels/defaultWidth;
       final float targetScaleDensity = targetDensity *(appScaleDensity/appDensity);
       final int  targetDensityDpi = (int) (targetDensity * 160);
       displayMetrics.density = targetDensity;
       displayMetrics.scaledDensity = targetScaleDensity;
       displayMetrics.densityDpi = targetDensityDpi;
       final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
       activityDisplayMetrics.density = targetDensity;
       activityDisplayMetrics.scaledDensity = targetScaleDensity;
       activityDisplayMetrics.densityDpi = targetDensityDpi;
   }
  • 不足之處:
    對(duì)于平板適配來(lái)說(shuō)不太友好,本質(zhì)上就是自動(dòng)拉伸控件的效果籽前。

  • 三方庫(kù):AutoSize

參考鏈接:
一種極低成本的Android屏幕適配方式
Android 簡(jiǎn)單好用的屏幕適配方案
Android 屏幕適配

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末亭珍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子聚假,更是在濱河造成了極大的恐慌块蚌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膘格,死亡現(xiàn)場(chǎng)離奇詭異桶癣,居然都是意外死亡患雇,警方通過(guò)查閱死者的電腦和手機(jī)味廊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)猫十,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)懂牧,“玉大人帝雇,你說(shuō)我怎么就攤上這事异袄≡弁鳎” “怎么了舶掖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵球昨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我眨攘,道長(zhǎng)主慰,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任鲫售,我火速辦了婚禮共螺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘情竹。我一直安慰自己藐不,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著雏蛮,像睡著了一般涎嚼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挑秉,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天法梯,我揣著相機(jī)與錄音,去河邊找鬼衷模。 笑死鹊汛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阱冶。 我是一名探鬼主播刁憋,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼木蹬!你這毒婦竟也來(lái)了至耻?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤镊叁,失蹤者是張志新(化名)和其女友劉穎尘颓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體晦譬,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疤苹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了敛腌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卧土。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖像樊,靈堂內(nèi)的尸體忽然破棺而出尤莺,到底是詐尸還是另有隱情,我是刑警寧澤生棍,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布颤霎,位于F島的核電站,受9級(jí)特大地震影響涂滴,放射性物質(zhì)發(fā)生泄漏友酱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一氢妈、第九天 我趴在偏房一處隱蔽的房頂上張望粹污。 院中可真熱鬧,春花似錦首量、人聲如沸壮吩。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸭叙。三九已至觉啊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沈贝,已是汗流浹背杠人。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宋下,地道東北人嗡善。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像学歧,于是被迫代替她去往敵國(guó)和親罩引。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容