Android屏幕適配和方案【整理】

版權聲明:本文爲HaiyuKing原創文章,轉載請註明出處!html

前言

這裏只是根據參考資料整理下,具體內容請閱讀參考資料。git

原型設計圖

推薦1倍效果圖,即採用 720 * 360 大小( 1280 *720:兩倍圖 \ 1920 * 1080: 三倍圖),最主要的緣由就是1px = 1dp,效果圖標多大的px,佈局就寫多大dp。github

屏幕各項參數

  • 手機像素(px):一個小黑點就是像素;
  • 手機尺寸:屏幕的對角線的長度;
  • 手機分辨率:整個屏幕一共有多少個點(像素),常見取值 480X800 ,320X480等;
  • 像素密度(dpi)

    1. 每英寸中的像素數(假如設備分辨率爲320*240,屏幕長2英寸寬1.5英寸,dpi=320/2 = 240/1.5 =160)
    2. 對應於DisplayMetrics類中屬性densityDpi的值;
    3. 固然這種寬和高的dpi都相同的狀況如今已經不多見,因此實際計算方式見下圖app

  像素密度(dpi)、屏幕尺寸、分辨率三者關係:ide

  

       舉例說明:屏幕分辨率爲:1920*1080,屏幕尺寸爲5吋的話,那麼dpi爲440:svg

     

  • 密度(density)

    1. 每平方英寸中的像素數(density = dpi / 160 );
    3. 對應於DisplayMetrics類中屬性density的值;
    4. 可用於px與px與dip的互相轉換 :dp = px / density ;工具

  

  720P,和1080P的手機,dpi是不一樣的,這也就意味着,不一樣的分辨率中,1dp對應不一樣數量的px(720P中,1dp=2px,1080P中1dp=3px)。佈局

  • 設備獨立像素(dp、dip)

    1.不一樣設備有不一樣的顯示效果,不依賴像素(dp = px / density =  px / (dpi / 160) )
    2.dpi(像素密度)爲160 的設備上1dp = 1px。字體

  • 放大像素(sp):用於字體顯示;
  • dp轉px、px轉dp:
public class Dp2Px {
    public static int dp2px(Context context, int dp) {
        return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5);
    }

    public static int px2dp(Context context, int px) {
        return (int) (px / context.getResources().getDisplayMetrics().density + 0.5);
    }
}

說明:0.5 是爲了不損失精度。this

適配分類

1、圖片適配

在AndroidStudio的資源目錄res下有五個層級圖片文件夾,分別用來存放不一樣分辨率的圖片:

  • drawable-ldpi :低分辨率(用的少了,通常再也不用)
  • drawable-mdpi:中分辨率
  • drawable-hdpi:高分辨率
  • drawable-xdpi:較高分辨率
  • drawable-xxdpi:超級高分辨率
  • drawable-xxxhpi:頂級分辨率

在對應的文件夾下放置不一樣分辨率的圖片就能夠很好的對圖片進行適配。
隨着屏幕愈來愈大,推薦xxdpi的一套切圖,這樣就能夠向下和向上兼容,節省資源。
建議圖標使用svg格式,圖片仍然使用png格式,svg的圖標大小約是png的1/4,在很大的項目中,圖標有不少,這個時候svg的優點就凸顯無疑了。

2、佈局適配

在layout以外,根據不一樣分辨率,建立不一樣的佈局文件夾:

  • layout-800 * 480
  • layout-1280 * 720

手機會根據分辨率去找設定的不一樣大小的layout的佈局。實際開發中這種使用的狀況很是少,由於佔用太多資源,慎用。

3、權重適配 

當兩個或者更多佈局佔滿屏幕寬或高的時候,子佈局可使用權重適配,常見於LinearLayout線性佈局中。

4、屏幕適配

詳細內容請閱讀參考資料。如下內容均是引用!

一、dp+自適應佈局+weight比例佈局直接適配

這基本是最原始的Android適配方案。

wrap_content,match_parent,layout_weight等,咱們就要絕不猶豫的使用,並且在高這個維度上,咱們要依照狀況設計爲可滑動的方式,或者match_parent,儘可能不要寫死。總之,全部的適配方案都不是用來取代match_parent,wrap_content的,而是用來完善他們的。

缺點

(1)這隻能保證咱們寫出來的界面適配絕大部分手機,部分手機仍然須要單獨適配;

  爲何dp只解決了90%的適配問題,由於並非全部的1080P的手機dpi都是480,好比Google 的Pixel2(1920*1080)的dpi是420,也就是說,在Pixel2中,1dp=2.625px,這樣會致使相同分辨率的手機中,這樣,一個100dp*100dp的控件,在通常的1080P手機上,可能都是300px,而Pixel 2 中 ,就只有262.5px,這樣控件的實際大小會有所不一樣。

(2)這種方式沒法快速高效的把設計師的設計稿實現到佈局代碼中,經過dp直接適配,咱們只能讓UI基本適配不一樣的手機,可是在設計圖和UI代碼之間的鴻溝,dp是沒法解決的,由於dp不是真實像素。並且,設計稿的寬高每每和Android的手機真實寬高差異極大,以咱們的設計稿爲例,設計稿的寬高是375px*750px,而真實手機可能廣泛是1080*1920;那麼在平常開發中咱們是怎麼跨過這個鴻溝的呢?基本都是經過百分比啊,或者經過估算,或者設定一個規範值等等。總之,當咱們拿到設計稿的時候,設計稿的ImageView是128px*128px,當咱們在編寫layout文件的時候,卻不能直接寫成128dp*128dp。在把設計稿向UI代碼轉換的過程當中,咱們須要耗費至關的精力去轉換尺寸,這會極大的下降咱們的生產力,拉低開發效率。

二、dimens基於px的適配(寬高限定符適配)

原理

根據市面上手機分辨率的佔比分析,咱們選定一個佔比例值大的(好比1280*720)設定爲一個基準,而後其餘分辨率根據這個基準作適配。

基準的意思(好比320*480的分辨率爲基準)是:
寬爲320,將任何分辨率的寬度分爲320份,取值爲x1到x320
長爲480,將任何分辨率的高度分爲480份,取值爲y1到y480

例如對於800 * 480的分辨率設備來說,須要在項目中values-800x480目錄下的dimens.xml文件中的以下設置(固然了,能夠經過工具自動生成):

<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="x1">1.5px</dimen>
<dimen name="x2">3.0px</dimen>
<dimen name="x3">4.5px</dimen>
<dimen name="x4">6.0px</dimen>
<dimen name="x5">7.5px</dimen>

能夠看到x1 = 480 / 基準 = 480 / 320 = 1.5 ;它的意思就是一樣的1px,在320/480分辨率的手機上是1px,在480/800的分辨率的手機上就是1*1.5px,px會根據咱們指定的不一樣values文件夾自動適配爲合適的大小。

 

缺點

第一,Android不一樣分辨率的手機實在太多了,可能你說主流就能夠,的確小公司主流就能夠,淘寶這種App確定不能只適配主流手機。
第二,控件在設計圖上顯示的大小以及控件之間的間隙在小分辨率和大分辨率手機上天壤之別,你會發現大屏幕手機上控件超級大。可能你會以爲正常,畢竟分辨率不一樣。但實際效果大的有些誇張。
第三,設計圖(好比360640)上的內容佔據屏幕的2/3,按照這種適配,特長手機(29601440 S8)上的內容也會佔據2/3,這確定不合理,控件之間的間隙會特別大,一看就不符合設計效果,手機長,內容佔據低於2/3才正常,好比可能佔據1/3.第四,佔據資源大:好幾百KB,甚至多達1M或跟多。

三、dimen 基於dp的適配(smallestWidth適配)

原理

這種適配依據的是最小寬度限定符。指的是Android會識別屏幕可用高度和寬度的最小尺寸的dp值(其實就是手機的寬度值),而後根據識別到的結果去資源文件中尋找對應限定符的文件夾下的資源文件。這種機制和上文提到的寬高限定符適配原理上是同樣的,都是系統經過特定的規則來選擇對應的文件。

舉個例子,小米5的dpi是480,橫向像素是1080px,根據px=dp(dpi/160),橫向的dp值是1080/(480/160),也就是360dp,系統就會去尋找是否存在value-sw360dp的文件夾以及對應的資源文件。

smallestWidth限定符適配和寬高限定符適配最大的區別在於,有很好的容錯機制,若是沒有value-sw360dp文件夾,系統會向下尋找,好比離360dp最近的只有value-sw350dp,那麼Android就會選擇value-sw350dp文件夾下面的資源文件。這個特性就完美的解決了上文提到的寬高限定符的容錯問題。

缺點

  • Android 私人訂製的緣由,寬度方面良莠不齊,不可能適配全部的手機。
  • sp和dp值有些值不全(這個應該是能夠解決的),姑且算是一個小問題。
  • 項目中增長了N個文件夾,上拉下拉查看文件很是不方便:想看string或者color資源文件須要拉不少再能到達。
  • 經過寬度限定符就近查找的原理,能夠看出來匹配出來的大小不夠準確。
  • 是在Android 3.2 之後引入的,Google的本意是用它來適配平板的佈局文件(可是實際上顯然用於diemns適配的效果更好),不過目前全部的項目應該最低支持版本應該都是4.0了(糗事百科這麼老的項目最低都是4.0哦),因此,這問題其實也不重要了。

四、今日頭條適配(修改手機的設備密度 density)

這套方案對老項目是不太友好的,由於修改了系統的density值以後,整個佈局的實際尺寸都會發生改變,若是想要在老項目文件中使用,恐怕整個佈局文件中的尺寸均可能要從新按照設計稿修改一遍才行。所以,若是你是在維護或者改造老項目,使用這套方案就要三思了。

原理

  經過修改density值,強行把全部不一樣尺寸分辨率的手機的寬度dp值改爲一個統一的值,這樣就解決了全部的適配問題。

  好比,設計稿寬度是360px,那麼開發這邊就會把目標dp值設爲360dp,在不一樣的設備中,動態修改density值,從而保證(手機像素寬度)px/density這個值始終是360dp,這樣的話,就能保證UI在不一樣的設備上表現一致了。

  UI設計圖是按屏幕寬度爲360dp來設計的,那麼在上述設備上,屏幕寬度其實爲1080/(440/160)=392.7dp,也就是屏幕是比設計圖要寬的。這種狀況下, 即便使用dp也是沒法在不一樣設備上顯示爲一樣效果的。 同時還存在部分設備屏幕寬度不足360dp,這時就會致使按360dp寬度來開發實際顯示不全。加上16:九、4:3甚至其餘寬高比層出不窮,寬高比不一樣,顯示徹底一致就不可能了。

  一般下,咱們只須要以寬或高一個維度去適配,好比咱們Feed是上下滑動的,只須要保證在全部設備中寬的維度上顯示一致便可,再好比一個不支持上下滑動的頁面,那麼須要保證在高這個維度上都顯示一致,尤爲不能存在某些設備上顯示不全的狀況。

  • 支持以寬或者高一個維度去適配,保持該維度上和設計圖一致;
  • 支持dp和sp單位,控制遷移成本到最小。

今日頭條的適配方式,今日頭條適配方案默認項目中只能以高或寬中的一個做爲基準,進行適配。

px = dp * density(dp是360dp),想要px正好是屏幕寬度的話,只能修改density。

/**
     * 適配:修改設備密度
     */
    private static float sNoncompatDensity;
    private static float sNoncompatScaledDensity;

    public static void setCustomDensity(@NonNull Activity activity, @NonNull final Application application) {
        DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
        if (sNoncompatDensity == 0) {
            sNoncompatDensity = appDisplayMetrics.density;
            sNoncompatScaledDensity = appDisplayMetrics.scaledDensity;
            // 防止系統切換後不起做用
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    if (newConfig != null && newConfig.fontScale > 0) {
                        sNoncompatScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {

                }
            });
        }
        float targetDensity = appDisplayMetrics.widthPixels / 360;
        // 防止字體變小
        float targetScaleDensity = targetDensity * (sNoncompatScaledDensity / sNoncompatDensity);
        int targetDensityDpi = (int) (160 * targetDensity);

        appDisplayMetrics.density = targetDensity;
        appDisplayMetrics.scaledDensity = targetScaleDensity;
        appDisplayMetrics.densityDpi = targetDensityDpi;

        final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
        activityDisplayMetrics.density = targetDensity;
        activityDisplayMetrics.scaledDensity = targetScaleDensity;
        activityDisplayMetrics.densityDpi = targetDensityDpi;

    }

只須要在baseActivity中添加一句話便可。適配就是這麼簡單。

DisplayUtil.setCustomDensity(this, getApplication());

缺點

  • 只須要修改一次 density,項目中的全部地方都會自動適配,這個看似解放了雙手,減小了不少操做,可是實際上反應了一個缺點,那就是隻能一刀切的將整個項目進行適配,但適配範圍是不可控的。這樣不是很好嗎?這樣原本是很好的,可是應用到這個方案是就很差了,由於我上面的原理也分析了,這個方案依賴於設計圖尺寸,可是項目中的系統控件、三方庫控件、等非咱們項目自身設計的控件,它們的設計圖尺寸並不會和咱們項目自身的設計圖尺寸同樣。當這個適配方案不分類型,將全部控件都強行使用咱們項目自身的設計圖尺寸進行適配時,這時就會出現問題,當某個系統控件或三方庫控件的設計圖尺寸和和咱們項目自身的設計圖尺寸差距很是大時,這個問題就越嚴重。

開源庫地址

一種極低成本的Android屏幕適配方式

AndroidAutoSize

參考資料

一種極低成本的Android屏幕適配方式

看完不會Android屏幕適配我跪搓板

Android 目前最穩定和高效的UI適配方案

騷年你的屏幕適配方式該升級了!-今日頭條適配方案

騷年你的屏幕適配方式該升級了!-smallestWidth 限定符適配方案

dpi 、 dip 、分辨率、屏幕尺寸、px、density 關係以及換算

相關文章
相關標籤/搜索