Android 屏幕適配的幾種方法

android中的dp在渲染前會將dp轉爲px,計算公式:

  • px = density * dp;

  • density = dpi / 160;

  • px = dp * (dpi / 160)

而dpi是根據屏幕真實的分辨率和尺寸來計算的,每個設備都可能不一樣的。

 

1.屏幕分辨率限定符適配

根據當前市面上手機的屏幕的分辨率創建不同的文件夾,系統運行的時候,會自動去選擇讀取對應的文件夾中的xml,即每種屏幕分辨率的設備需要定義一套 dimens.xml 文件

缺點是:假設我們UI設計圖是按屏幕寬度爲360dp來設計的,那麼在上述設備上,屏幕寬度其實爲1080/(440/160)=392.7dp,也就是屏幕是比設計圖要寬的。這種情況下, 即使使用dp也是無法在不同設備上顯示爲同樣效果的。 同時還存在部分設備屏幕寬度不足360dp,這時就會導致按360dp寬度來開發實際顯示不全的情況
而且上述屏幕尺寸、分辨率和像素密度的關係,很多設備並沒有按此規則來實現, 因此dpi的值非常亂,沒有規律可循,從而導致使用dp適配效果差強人意。

2.今日頭條適配方案(字節跳動)

在上面的兩種適配方案中,都是根據不同的屏幕大小設置不同的控件的寬高值。在公式 px =dp * density 中,不同屏幕的dp,以及density都是不同,從而實現不同的手機渲染有着不同的px。在今日頭條的適配方案的思路是假定每個手機的屏幕寬度是固定的。比如設計稿寬度是360dp, 想要保證在所有設備計算得出的px值都正好是屏幕寬度的話,只能修改 density 的值。

在xml中設置寬高後,都是通過以下代碼進行轉換:

http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/util/TypedValue.java#applyDimension

當在xml中設置的單位爲px, 則直接用對應px的值;當單位爲dp,則返回的是:value * metrics.density;當單位爲sp,則返回的是value * metrics.scaledDensity。今日頭條選用dp或者pt作爲適配單位,給出的理由是項目中大部分都是使用dp做單位。

如果UI小姐姐給的設計圖是 360dp(寬) x 640dp(高) 的設計圖,如果要適配所有的屏幕,則 density = 設備屏幕的真實寬度(單位:px) / 360。這裏爲什麼會是這樣呢?上面公式中:px =dp * density,假定了所有的屏幕的寬度都是360dp,而px的值就是設備屏幕的真實寬度,所以就有了:density = 設備屏幕的真實寬度(單位:px) / 360。這樣1dp在所有屏幕的寬中所佔的比率都是一樣的,都是1/360,在xml中就可以按照設計圖來定義了。

而對於字體,Google推薦設置的單位爲sp,在上面的源碼中,字體的轉換公式爲:px= value * metrics.scaledDensity,這裏的value 是在xml中定義的以sp爲單位的值。一般情況下,scaledDensity 的值與 density 是相等的。但是如果在系統中設置了改變了字體的大小,scaledDensity 的值與 density 就不相等了。scaledDensity = 人爲修改的density * (系統的ScaledDensity / 系統的Density)。如果不需要字體大小隨系統設置而改變,就直接使用dp做單位好了.

    /**
     * 使用 dp 作爲適配單位(適合在新項目中使用,在老項目中使用會對原來既有的 dp 值產生影響)
     * <br>
     * <ul>
     * dp 與 px 之間的換算:
     * <li> px = density * dp </li>
     * <li> density = dpi / 160 </li>
     * <li> px = dp * (dpi / 160) </li>
     * </ul>
     *
     * @param context
     * @param designSize 設計圖的寬/高(單位: dp)
     * @param base       適配基準
     */
    private static void matchByDP(@NonNull final Context context, final float designSize, int base) {
        final float targetDensity;
        if (base == MATCH_BASE_WIDTH) {
            targetDensity = sMatchInfo.getScreenWidth() * 1f / designSize;
        } else if (base == MATCH_BASE_HEIGHT) {
            targetDensity = sMatchInfo.getScreenHeight() * 1f / designSize;
        } else {
            targetDensity = sMatchInfo.getScreenWidth() * 1f / designSize;
        }
        final int targetDensityDpi = (int) (targetDensity * 160);
        final float targetScaledDensity = targetDensity * (sMatchInfo.getAppScaledDensity() / sMatchInfo.getAppDensity());
        final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
        displayMetrics.density = targetDensity;
        displayMetrics.densityDpi = targetDensityDpi;
        displayMetrics.scaledDensity = targetScaledDensity;
    }

    

 

從這兩次的效果圖,可以很明顯的看出加了適配方案後,兩個屏幕中控件的大小差不多一致了。此套方案對於原來的老項目不太友好,因爲改了屏幕的density,所有佈局的實際尺寸都會發生變化,整個佈局中的所有尺寸都要進行修改一遍。

ps:以上被整理成了一個單獨適配屏幕的類,下載地址是:https://download.csdn.net/download/and_caicai/12412196

參考:https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章