Android 屏幕適配剖析

前言

衆所周知,Android受權的廠商不可勝數,生產出的機型也數不勝數,致使尺寸碎片化很嚴重。固然,都9102年了,你們逐漸獲得了最優解,國內主流機型基本上都在720、1080、1440徘徊,最多高度上各有所長,可是仍是保留着很多其餘分辨率的手機,先來看一組數據(來源:友盟)——html

如圖所證上述結論的正確性,可是能夠看到,每一年都有比例不小的其餘尺寸的手機佔據着市場份額,更況且那些還在服役的古董機器。我相信,這部分用戶羣是不可能被產品經理所割捨的。git

爲了解決這個問題,咱們固然能夠——github

  1. 善於使用RelativeLayout、Linearlayout、ConstraintLayout;
  2. 合理使用wrap_content、match_content;
  3. 使用minHeight、minWidth、lines、ellipsize等等屬性;
  4. 使用dp、sp單位;
  5. 以某個頁面爲單位針對不一樣的手機使用不一樣的佈局、圖片、dimen;

可是我想說,以上種種,只是一個Android開發應該具有的基本素質。也許有人會問,這些還不夠嗎?並且dp、sp不已是官方適配過了的單位嗎?下面咱們就來逐步剖析。web

爲何官方須要使用設備獨立像素適配

設備獨立像素(dp、sp),又叫邏輯像素,是一種用縮放因子(scale)計算出來的、和像素有必定的換算比例的、不受設備分辨率和密度(ppi)制約的尺寸單位bash

那麼什麼是分辨率,什麼是ppi,什麼是dpi。微信

分辨率是指在手機屏幕中橫豎都有多少個像素點,所謂的1080x1920便是指,屏幕的高有1920個像素點,寬有1080個像素點。當咱們繼續查看手機參數的時候,會看到下一個指標,叫作ppi(Pixels Per Inch),表示每英寸所包含的像素點個數,ppi越大,屏幕越細膩,可是超過了肉眼的分辨率是沒有多大意義的,以榮耀10爲例,它的分辨率是1080x2280,那麼對角線所具備的像素點個數爲2522.86,而主屏尺寸是5.84英寸,那麼咱們能夠得出每英寸所包含的像素點個數爲431.997≈432,即ppi=432。那麼dpi又是什麼?dpi(Dots per Inch),字面意思是每英寸包含的點數,可是實際上它如今更多的用於表示顯示策略中的一個參數,在Android中,它是能夠在系統中設置的、是可變的、是用於計算縮放因子的,也許在不少文章中咱們均可以看到ppi就是dpi這樣的言論,可是其實它們已經和最初的釋義有所差別,具體參照WHAT IS DPI,我的認爲這篇文章是講述比較全面、靠譜、符合事實的。app

而獨立像素,爲何不受分辨率和密度制約?框架

咱們首先明白,當咱們假定的認爲像素點都是趨於正方形時,密度只能影響視覺呈現的物理大小和精細程度,屏幕上高寬都爲x個像素所組成的正方形,在相同分辨率不一樣密度的手機中,他們只是視覺大小不同,可是佔據屏幕的比例是一致的。ide

那麼咱們只須要分析爲何獨立像素不受分辨率制約。佈局

咱們知道,每一個手機出廠時都寫好了固定的dpi在手機的系統文件中,而dpi是造成獨立像素的一個重要參數,咱們能夠根據dpi計算出dp和px的換算比例,也就是縮放因子(scale),從官方的文檔,咱們能夠得出一個公式

1dp = 1px * scale = 1px * dpi / 160
複製代碼

也就是說,只要按照相同的規則控制dpi的數值,咱們在寬度的緯度上,能夠作到將全部分辨率都換算成一個數值的設備獨立像素。

打個比方,如今大部分720x1280的手機的dpi都等於320,根據公式可得,寬度爲360dp,而1080x1920的手機大部分的dpi都等於480,一樣根據公式所得寬度爲360dp。

那麼相同的設備獨立像素能夠作什麼呢?只要咱們設置成寬度爲180dp,那麼它永遠是佔據屏幕寬度一半的比例。

而如果咱們直接使用像素做爲控件的單位,那麼是沒法保證它在不一樣分辨率的手機是佔據相同的比例。

一樣舉個例子,在720x1280的手機上咱們設置寬度爲360px,它將佔據一半的屏幕,而在1080x1920的手機上只能佔據三分之一的屏幕。

這就是爲何官方推薦咱們使用設備獨立像素做爲尺寸的單位。那麼問題來了,既然設備獨立像素這麼優秀,那麼咱們——

爲何要二次適配

上節舉例中說到,大部分720x1280、1080x1920的手機寬度都是360dp,而大部分480x800的手機(dpi=240)寬度是320dp,那麼當設計稿是360dp的時候,會發生什麼。

舉個例子,以下圖所示,兩臺設備分辨率一致,可是dpi不一致,前者是480dpi,後者是540dpi(ps:不要問有沒有這種機器,nove4e就是這樣的),而設計稿是以360dp爲基準,熱度排名和貢獻排名的寬度比例是3:4,則咱們能夠看到其在320dp下表現比較差,即便咱們再如何佈局,如何使用屬性,它永遠是不完美的,由於它的邏輯寬度永遠都比設計稿少40dp。

而除了320dp、360dp,單國內手機的邏輯寬度就還有345.6dp、375.6dp、392.7dp、411.4dp、423.5dp等等。固然,理論上來講更多的邏輯寬度應該顯示更多的內容,然而現實的狀況每每不容許,這意味着——

  1. 須要設計多套圖;
  2. 開發工做繁重、維護困難;
  3. 增大包體積,畢竟不像iOS有App Slicing;

總之,就是人力成本過高。可是經過二次適配,咱們能夠作到一套設計圖適配「全部」設備,一套佈局「全家」適用。也許這不是最好的方案,可是綜合來看這是最合適的方案,是最具性價比的方案。那麼咱們要——

怎麼作二次適配

作二次適配的方法有多種,大致能夠分爲窮舉Hook

注:由於當下大部分app的應用場景只在於豎屏,即便有橫屏的界面也只須要保持高度不變,寬度自適應。退一步講,真的有個別頁面的高度也須要自適應時,能夠具體場景具體分析,即便不作二次適配,也是ok的。所以如下的適配方法只從寬度的緯度來說訴。

窮舉寬高限定符

咱們都知道,寬高限定符的匹配規則是,雙邊都小於屏幕分辨率的最接近的值。根據這個規則,咱們儘量的枚舉出全部的分辨率(雖然分辨率有不少,可是咱們只須要按照寬度來枚舉便可,高度設置成略大於寬度)。而根據testinwetest雲真機的分辨率,咱們能夠得出文件結構以下:

+-- res
|   +-- values
|   +-- values-330x320
|   +-- values-490x480
|   +-- values-550x540
|   +-- values-650x640
|   +-- values-730x720
|   +-- values-780x768
|   +-- values-810x800
|   +-- values-1100x1080
|   +-- values-1160x1152
|   +-- values-1210x1200
|   +-- values-1450x1440
|   +-- values-2170x2160
複製代碼

而後以1080px爲基準,計算出1px在其餘分辨率下的等比值 (注:默認values=values-1100x1080),假設目標分辨率的寬爲W,則公式爲:

px' = W/1080
複製代碼

舉個例子,720px的分辨率的dimens值爲:

<resources>
    <dimen name="x1">0.66px</dimen>
    <dimen name="x2">1.33px</dimen>
    <dimen name="x3">2.0px</dimen>
    <dimen name="x4">2.66px</dimen>
    <dimen name="x5">3.33px</dimen>
    <dimen name="x6">4.0px</dimen>
    <dimen name="x7">4.66px</dimen>
    <dimen name="x8">5.33px</dimen>
    <dimen name="x9">6.0px</dimen>
    <dimen name="x10">6.66px</dimen>
    .
    .
    .
    <dimen name="x1080">720px</dimen>
</resources>
複製代碼

配置好後,咱們從以上分辨率中選擇9種採樣,看看實際運行效果如何:

由上圖可見,運行結果是很是符合咱們預期值的,佔一半屏幕的仍是佔一半,熱度排名和貢獻排名的間距也基本差很少,惟一比較明顯的是每行的文字字數±1,這是因爲換算以後的像素有小數點形成的,可是這是能夠接受的。

而後咱們再來分析一下極端狀況,首先由於咱們是窮舉分辨率,因此此處不須要考慮dpi,又由於咱們窮舉的分辨率的寬是320-2160,因此咱們能夠從這個角度考慮邊界值:

  1. 寬低於320px,匹配默認values,此種狀況能夠認爲不存在;
  2. 寬等於某一個枚舉值(假設爲720px),高度剛好小於730px,匹配大一級的values,可是由於咱們設置的高度只是略大於寬,能夠認爲此種狀況不存在;
  3. 寬大於某一個枚舉值,可是小於下一級的寬度(假設爲1600px),匹配values-1450x1440;
  4. 寬小於某一個枚舉值,可是大於上一級的寬度(假設爲1300px),匹配values-1210x1200,等同於第3點;
  5. 寬大於2160px,匹配values-2170x2160,等同於第3點;

咱們再看看三、四、5的運行效果如何:

從結果能夠看出,咱們出現的極端狀況都是比預期值要寬,這是由於咱們分辨率限定符是向下匹配的。

綜上所述,咱們得出結論:

  1. 在已知分辨率的設備中,此方法基本能夠完美適配機型;
  2. 全部地方都建議統一使用px',包括字體、自定義控件等,不然就會不兼容;
  3. 由於字體也須要使用px',因此app字體大小不會受到系統設置——字體顯示大小的影響;
  4. 由於使用了px',代碼中動態設置大小間距等等要額外注意單位;
  5. 由於非1080px是換算比例,必然存在小數點,所以會存在一丟丟偏差;
  6. 枚舉分辨率太多,致使dimens文件過多,包體積會增大一點,若是1080個px所有作映射的話,以示例中的枚舉值大概要多0.5MB左右;
  7. 由於存在場外分辨率,即便使用了此適配,依然不能夠盲目的用絕對值,仍是要配合其餘控件屬性一併使用;
  8. 侵入性比較高,依賴技術人員的素養;

窮舉最小寬度限定符

最小寬度限定符,是指在邏輯寬度上限定使用小於而且最接近於屏幕寬度的資源。而邏輯寬度(W')能夠從分辨率(W)和dpi得知:

W' = W / ( dpi / 160 )
複製代碼

咱們能夠和窮舉分辨率限定符同樣,窮舉出全部可能的邏輯寬度。爲了分析,咱們暫定文件結構以下:

+-- res
|   +-- values
|   +-- values-sw320dp
|   +-- values-sw360dp
|   +-- values-sw411dp
複製代碼

而後以360dp爲基準,計算出1dp在其餘邏輯寬度下的等比值 (注:默認values=values-sw360dp),假設目標邏輯寬度爲W,則公式爲:

dp' = W/360
複製代碼

一樣的舉個例子,320dp的邏輯寬度的dimens值爲:

<resources>
    <dimen name="dp_1">0.89dp</dimen>
    <dimen name="dp_2">1.78dp</dimen>
    <dimen name="dp_3">2.67dp</dimen>
    <dimen name="dp_4">3.56dp</dimen>
    <dimen name="dp_5">4.44dp</dimen>
    <dimen name="dp_6">5.33dp</dimen>
    <dimen name="dp_7">6.22dp</dimen>
    <dimen name="dp_8">7.11dp</dimen>
    <dimen name="dp_9">8.00dp</dimen>
    <dimen name="dp_10">8.89dp</dimen>
    .
    .
    .
    <dimen name="dp_360">320dp</dimen>
</resources>
複製代碼

咱們來看看實際的運行效果:

顯而易見,又是符合咱們預期的,可是不可避免的是邏輯寬度依舊存在刺頭(緣由見文章開頭——爲何要二次適配),好比384dp(Nexus 4)、392dp(XiaoMi MIX2),因此咱們再次來看看極端狀況:

  1. 邏輯寬度小於320dp,雖然沒有數據支撐,可是咱們假定的認爲這已是最小的寬度了,或者說,總有一個最小值(後續等統計出來,補上相關數據);
  2. 邏輯寬度位於兩個枚舉值之間,好比384dp;
  3. 邏輯寬度大於411dp;

好了,由於最小寬度限定符依舊是向下匹配的,從而又回到了和上一節如出一轍的狀況——極端狀況比預期值要寬,因此此處咱們再也不重複貼圖。

那麼咱們總結一下此節:

  1. 在已經枚舉的邏輯寬度中,基本能夠完美匹配設備;
  2. xml、代碼、包括自定義控件中都須要使用dp'的引用來保持一致;
  3. 須要再配置一套TextSize,至因而用dp仍是sp,仁者見仁智者見智(微信沒有用sp);
  4. 枚舉值映射的dp'值比寬高限定符少;
  5. 相比寬高限定符兼容性略高,即便有些地方直接寫成了dp,也是能夠的;
  6. 一樣由於是換算比例,必然存在小數點,最後應用成px時,也可能存在一丟丟的偏差;
  7. 包體積和dimens的枚舉數量成正比;
  8. 一樣存在場外dpi,不能夠盲目的用絕對值;
  9. 侵入性比較高,依賴技術人員的素養;

上面說了如何窮舉來進行適配,可是如何窮舉的既完整又簡潔是一個難點,那麼可不可能有一個測量的終點,全部的間距、大小、尺寸都會經過這裏,咱們在這個終點進行自動化適配就行了?固然是有的。

選擇onMeasure進行Hook

咱們知道,view是須要先measure而後layout而後才draw的,那麼切入點就來了——onMeasure。

典型的例子是AndroidAutoLayout,它的使用方法詳見ReadMe,這裏再也不贅述。其核心思想是即是經過重寫其onMeasure,在調用super.onMeasure(widthMeasureSpec, heightMeasureSpec)以前從新根據屏幕寬度及高度設置了相關屬性的值,如padding、margin、height、width、textSize。

固然,AndroidAutoLayout的上一次提交代碼已是在4 yeas ago,在它的設計之初,是假定的認爲全部的手機的高寬比都是在一個恰當的範圍,好比720x1280,因此它的高寬都分別進行了不一樣比例的縮放適配。然而,9102年,手機的高寬比顯然已經多種多樣。因此,AndroidAutoLayout已經進入了它的侷限性。可是,它依舊是咱們能夠借鑑的目標,咱們只須要將其高按照寬的縮放比例來縮放,或高或矮的手機自適應高度便可。(有興趣的同窗能夠嘗試一下,這裏只講如何hook~)

那麼,onMeasure中怎麼hook呢?在AndroidAutoLayout中,它寫了不少的自定義ViewGroup,好比AutoLinearLayout、AutoRelativeLayout、AutoFrameLayout,其實裏面的代碼都大同小異,咱們以AutoLinearLayout爲例——

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (!isInEditMode()) { //這句代碼不用管,用來判斷是不是IDE預覽模式的
        mHelper.adjustChildren();
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
複製代碼

能夠看到,AndroidAutoLayout在super.onMeasure以前只作了一件事,就是adjustChildren,修改控件屬性值。

public void adjustChildren() {
    AutoLayoutConifg.getInstance().checkParams(); //這句話不用管,用來檢查庫配置的

    for (int i = 0, n = mHost.getChildCount(); i < n; i++) {
        View view = mHost.getChildAt(i);
        ViewGroup.LayoutParams params = view.getLayoutParams();

        if (params instanceof AutoLayoutParams) {
            AutoLayoutInfo info = ((AutoLayoutParams) params).getAutoLayoutInfo();
            if (info != null) {
                info.fillAttrs(view);
            }
        }
    }
}
複製代碼

而adjustChildren中循環取到了全部表層childView的AutoLayoutParams,AutoLayoutParams繼承於父類LayoutParams,它也沒幹啥事,主要是將須要適配的屬性(如textSize)存儲起來。

public static AutoLayoutInfo getAutoLayoutInfo(Context context,AttributeSet attrs) {
    ...
    //原來設計的時候是和寬度的相關的屬性按寬度縮放,和高度相關的屬性按高度縮放。可是總有特例,因此baseWidth、baseHeight就是用來強制約束縮放方向的。
    int baseWidth = a.getInt(R.styleable.AutoLayout_Layout_layout_auto_basewidth, 0);
    int baseHeight = a.getInt(R.styleable.AutoLayout_Layout_layout_auto_baseheight, 0);
    ...
    for (int i = 0; i < n; i++) {
        ...
        switch (index) {
            case INDEX_TEXT_SIZE:
                info.addAttr(new TextSizeAttr(pxVal, baseWidth, baseHeight));
                break;
            case INDEX_PADDING:
                info.addAttr(new PaddingAttr(pxVal, baseWidth, baseHeight));
                break;
            ...
        }
    }
    return info;
}
複製代碼

固然,不一樣的AutoAttr會實現各自的縮放方法,其實就是很簡單的計算出設計稿寬高和屏幕寬高的比值,而後和attribute的原始值相乘,獲得最終的屬性值。

public void apply(View view) {
    int val;
    if (useDefault()) {
        val = defaultBaseWidth() ? getPercentWidthSize() : getPercentHeightSize();
    } else if (baseWidth()) {
        val = getPercentWidthSize();
    } else {
        val = getPercentHeightSize();
    }
    if (val > 0) {
        val = Math.max(val, 1);//for very thin divider
    }
    execute(view, val);//執行縮放,並將其設置到view或者layoutParams中
}
複製代碼

而apply在哪裏被調用,其實就是在adjustChildren中的fillAttrs。

這樣,hook就完成了。固然,AutoAttr、Helper、AutoLayoutParams以及內部封裝的AutoLayoutActivity、AutoUtils,這些都是設計思想,重要的是實現思路,其實你也能夠簡單粗暴的糅合在一塊兒,啊哈哈~

既然是完美適配,那就隨便貼幾個亂七八糟的分辨率吧~

按照慣例,如下是此節總結:

  1. 經過onMeasure做爲切入點,基本能夠完美適配屏幕,而且幾乎沒有性能損耗;
  2. 一樣1也是缺點,必需要實現的viewGroup、attribute才支持;
  3. 對於自定義控件,適配工做會很繁重;
  4. 對於須要修改屬性的控件,同3;
  5. 目前AndroidAutoLayout對於動態添加的控件不是很友好,須要添加完以後手動調用AutoUtils.auto(view),這會帶來額外的開銷,固然使用者能夠自行拓展來支持這一點;
  6. 侵入性一樣比較高,很是依賴技術人員的素養;
  7. 原來的庫,高寬是以不一樣比例縮放的,可是如今的手機高寬比差別都比較大,那麼依照老規則顯示效果會很是差,雖然能夠強制指定控件的縮放方向,可是工做量會比較繁瑣,所以可能須要本身修改一下源碼;
  8. 固然,AndroidAutoLayout攔截替換的是px,若有須要,是能夠換成攔截dp、sp的,這個不重要,重要的是hook點。

話說回來,其實onMeasure是一個淺層次的hook點,它雖然優勢很明顯,可是一樣的缺點也很明顯,那有沒有一個切入點,既能夠自動適配,又不用寫這麼多代碼,侵入性也不高呢?字節跳動團隊給了咱們答案

選擇DisplayMetrics.densityDpi進行Hook

前面有講到dp和px之間的關係,咱們能夠知道:

1dp = 1px * dpi / 160
複製代碼

而系統必定有一個地方是用來轉換這些單位的,好比TypedValue中:

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;
}
複製代碼

好比BitmapFactory中:

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; 
        }
    }
    
    //inDensity是指資源所在的drawable文件夾的密度,inTargetDensity是指屏幕密度
    if (opts.inTargetDensity == 0 && res != null) {
        opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
    }
    
    return decodeStream(is, pad, opts);
}

複製代碼

咱們觀察能夠看到,他們都用到了DisplayMetrics.densityDpi這個屬性,那麼咱們只須要根據屏幕密度來修改這個屬性值,就能夠僞裝屏幕永遠爲360dp的邏輯寬度。

而DisplayMetrics能夠從三個地方獲得:

// 系統的屏幕尺寸
val systemMetrics = Resources.getSystem().displayMetrics
// application的屏幕尺寸
val applicationMetrics = application.resources.displayMetrics
// activity的屏幕尺寸
val activityMetrics = activity.resources.displayMetrics
複製代碼

咱們只須要修改application和activity的就行,system則建議不修改,用於保留一份原始數據,並且即便改了也沒什麼用,它是用於獲取系統資源的。

從上文咱們能夠很容易的得知當前屏幕的邏輯寬度是:

/**
 *  注意這裏widthPixels不要用real width,緣由有二:
 *  1.available display width可能會小於real width,雖然大部分實際場景中是同樣的;
 *  2.在1的場景中(好比說屏幕左右有個裝飾欄?),咱們可能會出現,application中應用的是available display width,activity中應用的是real width,
 *  可是若是activity中使用了application.resource,那麼此時間距大小會略小,這並無什麼關係。
 *  反過來若是咱們修改的時候用的是real width,那麼此時就會顯示不下。
 */
val widthInDp = resources.displayMetrics.run {
    widthPixels / (densityDpi / 160) 
}
複製代碼

那麼咱們能夠設置densityDpi爲:

val targetDpi = resources.displayMetrics.widthPixels * 160 / 360  //360是咱們的設計稿的邏輯寬度
val sysMetrics = Resources.getSystem().displayMetrics

resources.displayMetrics.run {
    densityDpi = targetDpi
    density = targetDpi / 160f
    scaledDensity = density * sysMetrics.scaledDensity / sysMetrics.density //由於用戶會修改字體大小,所以須要根據原比例來獲得新的scaledDensity
}

application.resources.displayMetrics.run {
    densityDpi = targetDpi
    density = targetDpi / 160f
    scaledDensity = density * sysMetrics.scaledDensity / sysMetrics.density
}
複製代碼

恢復的時候使用:

/**
 * 這裏直接使用sysMetrics進行恢復,緣由有二
 * 1.不用記錄中間值
 * 2.在使用應用時若是修改了系統字體大小,sysMetrics會同步修改,不用再監聽registerComponentCallbacks
 */
val sysMetrics = Resources.getSystem().displayMetrics

resources.displayMetrics.run {
    densityDpi = sysMetrics.densityDpi
    density = sysMetrics.density
    scaledDensity = sysMetrics.scaledDensity
}

application.resources.displayMetrics.run {
    densityDpi = sysMetrics.densityDpi
    density = sysMetrics.density
    scaledDensity = sysMetrics.scaledDensity
}
複製代碼

看看適配效果~

看起來perfect是否是?代碼也很簡單是否是?好像也沒什麼侵入性是否是?然而萬物有利也有弊:

  1. 若是咱們修改了application.resource,若是三方庫有用到,會受影響;
  2. 若是咱們是經過registerActivityLifecycleCallbacks修改的activity.resource,那麼三方庫的activity會受影響;
  3. 沒法控制三方庫可能也會同時修改;
  4. 系統控件也會受到影響,好比toast,因此特別不建議將設計稿的邏輯寬度設置的比較極端,好比爲了從設計稿照抄省事,將其設置成1080dp;
  5. webView初始化的時候會還原density的值致使適配失效,須要修改以下:
    /**
     * 繼承webView,複寫此方法
     **/
    override fun setOverScrollMode(mode: Int) {
        super.setOverScrollMode(mode)
        adaptDensityDpi()
    }
    
    /**
     * 或者複寫activity的此方法
     */
    override fun getResources(): Resources {
        adaptDensityDpi() //注意避免死循環及重複修改
        return super.getResources() 
    }
    複製代碼
  6. 某些系統可能由於框架修改致使修改DisplayMetrics失效,好比MIUI7 + Android5.1.1;
  7. 須要考慮特殊activity不須要適配;
  8. 由於fragment其實用的是activity的resource,若是其中一個fragment不須要適配,那麼須要考慮切換fragment時適配重置;

因此以上的代碼其實只能應用於demo當中,以期證實咱們的方向是對的。剩下的咱們還須要進一步解決上述列出的這些問題,還須要適配,須要封裝,須要提升易用性、健壯性~好比AndroidAutoSize

收筆

好了,本文到此結束。說了這麼多,我的仍是比較傾向於最後一種方案。固然,一個方案的成熟是須要成長的,項目也是,人也是。其餘的,仁者見仁,智者見智咯。


本文做者: timedance
本文連接: www.tktimedance.com/posts/a5563…
Demo地址: github.com/timedance/S… 版權聲明: 本博客全部文章除特別聲明外,均採用 BY-NC-ND 許可協議。轉載請註明出處!

相關文章
相關標籤/搜索