android 適配筆記

ps: 適配啊對于 Android 來說永遠不會過時

相關概念

屏幕尺寸
  • 含義:手機對角線的物理尺寸
  • 單位:英寸(inch),1英寸=2.54cm

Android手機常見的尺寸有5寸、5.5寸、6寸等等

px 屏幕分辨率
  • 含義:手機在橫向、縱向上的像素點數總和
    一般描述成屏幕的"寬x高”=AxB,例如 1080x1920,即寬度方向上有1080個像素點,在高度方向上有1920個像素點
  • 單位:px(pixel),1px=1像素點
    UI設計師的設計圖會以px作為統一的計量單位,常見分辨率有:320x480、480x800、720x1280、1080x1920、1080*2160
dp 屏幕像素密度

Android 特有的單位,注意這是像素密度

  • 含義:每英寸的像素點數
  • 單位:dpi(dots per ich)
  • 計算方式
    例如現在有一臺手機如下:分辨率 1080*1920,屏幕物理大小 5英寸。先算出對角線的長度=2203,然后除以5=440dp,dp 就是這么算出來的,詳細看圖:


    944365-5f2509be9276460c.png

    944365-2b5dc928ab334440.png

Android 常用的圖片文件夾和 dp 關系看下圖:


1447577693400_7.png

android 適配的困境

android 的適配是很困難的,為啥,很簡單 google 早期預想的 dp 適配體系現在完全沒用,要靠自己做適配,這樣能不困難嗎

Android 碎片化現狀:

  • 定制系統奇多
    由于Android系統的開放性,任何用戶、開發者、OEM廠商、運營商都可以對Android進行定制,于是出現了小米定制的MIUI、魅族定制的flyme、華為定制的EMUI等等
  • 屏幕尺寸和分辨率組合復雜
    * 屏幕尺寸有 4.0寸,4.2,4.5,4.8,5.0,5.2,5.5,5.8,6.0,6.1,6.2,6.8,7
    * 屏幕分辨率有 320x480、480x800、720x1280、1080x1920,1080*2160

Android 早期規定以 160dp(即屏幕分辨率為320x480)為基準:1dp=1px,然后配合 google 官方建議的上述屏幕分辨率和物理尺寸,同分辨率,物理尺寸下的手機擁有相同的 dp ,可以計算出 dp 和 px 的轉換值,然后利用不同 dp values 文件下的 dimens 文件做適配

想象很美好,但是實際手機各方面技術發展很快,完全超過了 Google 的設想,手機廠商也不鳥 google 的建議規范,上面這么多 分辨率和屏幕尺寸的組合,讓 手機基本都處于不同的 dp 值狀態,這些 dp 值變化沒有規律,所以讓 dp 適配成為不可能,dp 值太多,無法做到適配

最簡單的就是不同的手機基本有不同的 dp 值,寬和高的 dp 都不一樣,一個手機 320dp * 480dp ,另一個手機 360dp * 560dp,寬和高的 dp 數不一樣,變化沒有規則,那怎么能再用 dp 做適配呢


Android 資源與資源限定符

Android 在不同環境下會選擇不同的資源,比如在不同的分辨率下,使用不同的圖片,Android 的資源體系是 Android 適配的基礎也是核心

android 資源種類如下:


20160615191144010.png
  • animator
    用于定義屬性動畫的 XML 文件
  • anim
    定義漸變動畫的 XML 文件
  • color
    用于定義顏色狀態列表
  • drawable
    位圖文件(.png、.9.png、.jpg、.gif)、狀態列表、形狀...
  • mipmap
    適用于不同啟動器圖標密度的 mipmap/ 文件夾管理啟動器圖標的詳細信息。
  • layout
    用于定義用戶界面布局。
  • menu
    用于定義應用菜單(如選項菜單、上下文菜單或子菜單)的 。
  • raw
    要以原始形式保存的任意文件(2.3版本要求1M)。
  • values
    包含字符串、整型數和顏色等簡單值的
  • xml
    可以在運行時通過調用 Resources.getXML() 讀取的任意 XML 文件

android 資源限定符如下:


Snip20180709_1.png

資源的適配是按照上述圖中順序位為優先級,一步一步塞選出符合條件的文件夾,詳細看下圖:


20160615191159339.png

比如有以下幾個適配需要做:

  • 區域設置 = en-GB
  • 屏幕方向 = port
  • 屏幕像素密度 = hdpi
  • 觸摸屏類型 = notouch
  • 主要文本輸入法 = 12key

然后我們根據上述要求生成相應文件夾,以 drawable 為例:

  • drawable-en/
  • drawable-en-port/
  • drawable-en-notouch-12key/
  • drawable-land-hdpi/
  • drawable-port-notouch-12key/

資源適配文件夾命名規范:

  • 按照優先級寫配置
  • 可以支持多級配置,中間以 - 鏈接,沒有空格

除了 drawable 文件夾,res 下面的所有文件夾都進行資源適配

大家都是很聰明的,雖然我寫的不是很清晰,但是我想大家都應該能夠理解,畢竟這個點不難不是,難在我們沒有系統接觸過,相關資源也很少

搞清楚了 Android 的資源適配,基本 Android 適配就穩了,android 的適配思路就是就是利用各種工具或是方式,針對不同的條件書寫所有的適配資源文件夾,本質永遠是資源適配

常見我們使用的是 sw 和 分辨率 2種適配思路:


5382223-4f42a5637f53f582.png
5382223-c59b8353245a0ebd.png

適配方案一覽

常見思路:

  • 傳統 dimens 思路
  • layout 布局內適配
  • 根據分辨率提供不同 layout 布局
  • gogole 百分比布局庫

來自人民群眾的智慧:

  • ScreenAdaptation
  • AutoLayout
  • Rudeness
  • 批處理自動生成 px,dp 的百分比 dimens 文件夾

經典的適配思路

對于經典的適配方式,這里借助鴻神的文章:

第一篇:主要是根據設計圖的尺寸,然后將設計圖上標識的px尺寸,轉化為百分比,為所有的主流屏幕去生成對應百分比的值,每個尺寸都會有一個values文件夾。存在一些問題:產生大量的文件夾,適配不了特殊的尺寸(必須建立默認的文件夾)

第二篇和第三篇:這兩篇屬于一樣的了,主要是基于Google推出的百分比布局,已經很大程度解決了適配的問題。存在一些問題:使用起來比較反人類,因為設計圖上標識的都是px,所以需要去計算百分比,然后這個百分比還是依賴父容器的,設計圖可能并不會將每個父容器的尺寸都標識出來,所有很難使用(當然,有人已經采用自動化的工具去計算了)。還有個問題就是,因為依賴于父容器,導致ScrollView,ListView等容器內高度無法使用百分比。


傳統 dimens 思路

其實這個不用我多說了吧,就是在不同配置環境是中配置 id 相同,但是數值不同的 dimens 參數,已實現適配。早先 google 建議我們在 android 的 m,h,l,x,xx,xxx values 文件夾中去配 dimens 參數,但是現在這個好使了, 這幾種 dp 的配置早就不嗯呢乖覆蓋所有的 android 設備了。最近幾年出現了很多 dimens 思路的變種,思路和傳統 dimens 一樣,區別在于可以適配更多的設備


layout 布局內適配

這個思路大家都做過,就是利用不同布局自身的特殊屬性來動態是適配屏幕的變化,本質上利用布局 viewGroup 的 onMeasu,onLayout 動態計算來實現

我們常見的布局有下面幾種:

  • 幀布局(FrameLayout)
  • 絕對布局(AbsoluteLayout)
  • 線性布局(Linearlayout)
  • 相對布局(RelativeLayout)
  • 約束布局 (ConstraintLayout)

常見的手段比如:

  • 使用 Linearlayout 的 weight 比重保持控件之間的寬或高關系
  • 使用 RelativeLayout 的相對位置排版
  • 使用 ConstraintLayout 的 view 寬高比例,chain view 鏈,比重,輔助線

這些手段只是實現的 layout 頁面級別的一些適配,但是不是整個 andoid 的基礎,另外使用這些適配手段會造成 viewGroup 對自身的對此計算,布局,明顯加重布局顯示任務量,會造成頁面卡頓

對于這些手段來說,我們不要過多依賴,想還是要繼續探索 android 適配更合理的方式


AutoLayout

鴻神開源的適配庫,使用很簡單,但是可惜不更新了,另外有人反應多少有點小問題,同時該庫也存在擴展性較差。對于每一種ViewGroup都要對應編寫對應的AutoLayout進行擴展,對于各View的每個需要適配的屬性都要編寫代碼進行適配擴展;在onMeasure階段進行數值計算。消耗性能,并且這對于非LayoutParams中的屬性存在較多不合理之處。

這個適配方案是需要我們 activity 繼承 AutoLayoutActivity 即可,然后在 xml 中使用 UI 圖中的 px 值就可以了,然后我們就可以根據下面這張最常見的 UI 圖用 px 做適配了


20151123092142493.jpeg

在 app 項目中的 AndroidManifest 中注明注冊設計圖尺寸即可

<meta-data android:name="design_width" android:value="768"></meta-data>
<meta-data android:name="design_height" android:value="1280"></meta-data>

詳細的去看鴻神的文章:


ScreenAdaptation

這是簡書上一個朋友的思路,核心就是重置屏幕密度。UI 給一個模板圖,使用 dp 單位,然后我們根據這個標準圖的分辨率和屏幕密度為標準,就算和安裝機器的縮放比例,然后修改安裝機器的屏幕參數以達到適配的目的。核心代碼如下:

/**
     * 重置屏幕密度
     */
    public static void resetDensity(Context context) {
        //繪制頁面時參照的設計圖尺寸
        final float DESIGN_WIDTH = 800f;
        final float DESIGN_HEIGHT = 1280f;
        final float DESTGN_INCH = 5.0f;
        //大屏調節因子,范圍0~1,因屏幕同比例放大視圖顯示非常傻大憨,用于調節感官度
        final float BIG_SCREEN_FACTOR = 0.8f;

        DisplayMetrics dm = context.getResources().getDisplayMetrics();

        //確定放大縮小比率
        float rate = Math.min(dm.widthPixels, dm.heightPixels) / Math.min(DESIGN_WIDTH, DESIGN_HEIGHT);
        //確定參照屏幕密度比率
        float referenceDensity = (float) Math.sqrt(DESIGN_WIDTH * DESIGN_WIDTH + DESIGN_HEIGHT * DESIGN_HEIGHT) / DESTGN_INCH / DisplayMetrics.DENSITY_DEFAULT;
        //確定最終屏幕密度比率
        float relativeDensity = referenceDensity * rate;

        if (ORIGINAL_DENSITY == -1) {
            ORIGINAL_DENSITY = dm.density;
        }
        if (relativeDensity > ORIGINAL_DENSITY) {
            relativeDensity = ORIGINAL_DENSITY + (relativeDensity - ORIGINAL_DENSITY) * BIG_SCREEN_FACTOR;
        }
        dm.density = relativeDensity;
        dm.densityDpi = (int) (relativeDensity * DisplayMetrics.DENSITY_DEFAULT);
        dm.scaledDensity = relativeDensity;
    }

詳細的請看作者的文章:

試了試 demo 的效果,demo 用的是 dp ,不同設別還原還可以,但是對于屏幕比例變動的就不行了,會產生拉伸或壓縮的問題。

雖說我們在 xml 中寫的是 dp,但是系統在渲染時還是會最終轉換成 px 的,所以就是利用這個思路

舉個例子:

  • 比如有 2個設備 A / B,像素密度 A 160 dp ,1dp = 1px ;B 480 dp ,1dp = 3px。
  • 有一個 view 占屏幕大小的 1/4 ,那么 1/4 在 A / B 分別有多大呢
  • 在 B 中 1/4 = 240 dp * 240 dp
  • 在 A 中 1/4 = 80 dp * 80 dp
  • 那么保持占用體積比例不變,B 的 view 寬高需要縮小 3 倍 ,這個 3 就是 A 和 B 之間的像素密度倍數
  • 那么就可以推導出,像素密度高的 view 需要縮小像素密度倍數才能在保持 view 在低像素密度時 view 占用空間比例不變
  • 但是現在使用的高像素密度設備 view 的寬高值,那么為了保證 Android 系統最后計算出的 px 值正確,我們只能等比例縮小所在設備的 displayMetrics.density 和 displayMetrics.densityDpi

上面的這個開源庫,別看寫的麻煩, 基本就是這個思路


Rudeness

也是一種百分比的思路,和 ScreenAdaptation 的思路一樣,但是實現不一樣,是以寬為基準匹配,不考慮高的因素,借助了 pt 這個單位,利用的是 android 系統中對于不同單位的換算公式,在其中插入百分數以實現動態計算百分比對應的 px 值的目的

系統單位轉換公式: TypedValue#applyDimension(int unit, float value, DisplayMetrics metrics)

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

我們只要在借助系統的單位換算公式,使用里面的某個參數可以實現注入我們需要的計算邏輯的公式。我們選擇 xdpi 這個參數注入邏輯,designWidth 是 UI 圖寬 px 值

Point size = new Point();
activity.getWindowManager().getDefaultDisplay().getSize(size);
resources.displayMetrics.xdpi = screenPoint.x / autoWidth * 72f

我們借助 pt 這個單位,然后修改 xdpi 這個參數,在期中除以 designWidth ,就能間接的實現百分比計算了。

詳細請這里:

關于 pt 適配思路,我之后又研讀一次,自己寫了工具出來,有興趣可以看:


批處理自動生成 px,dp 的百分比 dimens 文件夾

這是我上個公司以為前輩的方案,根據 UI 圖出算對應的所有百分比在不同的 dp 環境下 values 中 demens 中對應的 dp 值,然后我們引用。

這個方案得到的值不是很精確,但是基本可以滿足需求,沒有上面2個開源方案面對的系統不知道什么時候把顯示矩陣參數還原的問題。

示意圖:


Snip20180712_2.png

這是前輩寫的 java 類,也是以寬為基準,編輯 java 文件填寫相應的 UI 圖分辨率,然后運行就會輸出相應的所有 values 文件夾,放到項目中就可以用了。時間有點長了,這個 java 類我找找,找到我會放上來的

后來我發現早就有人寫了這個方案,分為 px 適配和 dp 適配,px 的思路和我前輩的一樣,dp 的需要自己算 UI 標準 dp 才行

px 適配看:


全面屏適配

部分黑屏處理

全面屏適配為啥還要適配,因為全面屏的寬高比比較高,一般是 18:9 ,超過系統默認值

Android 配置文件里 application 中有一個元素 max_aspect ,這是 Android 系統支持的最大寬高比,默認是 1.86 = 16:9,超過這個數,屏幕的高就有部分會黑屏無法利用了,下面是個例子:


Snip20180706_26.png

好在,可以設置這個參數

  <application
        android:maxAspectRatio="2.1"
        android:name=".MyApplication">

max_aspect 設置為 2.1 = 18.5:9 ,基本就能應對現在的全面屏手機的屏幕寬高比了,不夠還可以往上加

之后我看到了,在配置文件中設置 resizeableActivity = true 就可以不用 maxAspectRatio 了,貌似現在都有 19.5:9 的屏幕了,2.1不夠了,大家設置的大一些

  <application
        android:maxAspectRatio="2.3"
        android:resizeableActivity="true">

application maxAspectRatio 屬性 26 以上才支持,所以保險起見,大家還是在 配置文件里加 mate-data 吧



啟動頁處理

全名平帶來了新的屏幕寬高比 - 18:9,在啟動頁時,我們都是全屏顯示的,新的分辨率要求啟動頁的背景圖適應新的寬高比


cmOLumrNib1fIKemR6pK1gNB1Sgg6Ct0aicK1viaUVCshwGWUbuzhm3ib6ibXFyDIVTx2QK2uY2410VrdSqNvRPFU7g.jpeg

處理思路無外乎條件系統的資源文件夾和 .9 適配了,不過 .9適配需要 UI 作圖時考慮給我們留邊,要不我們也沒放拉伸,新的圖片文件夾這樣建 - drawable-xxhdpi-2160x1080 、drawable-long


劉海屏適配

國內的劉海屏時刻在太坑了,國內廠商早在 google 之前就用上了 劉海屏 ,所以 google 在 android p 28 上提供的方法就別想用了啦,沒戲,我們之只能挨個商家去適配...

這方面有非常好的文章,我就不復制了,大家直接看下面的資料把

google P 適配思路:

華為,小米,oppo,vivo 適配四思路


參考資料:

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