1. 程式人生 > >螢幕適配方案

螢幕適配方案

使用

直接在Application的onCreate方法中呼叫(Density類直接參照附錄)

Density.setDensity(this, 375f);

這個地方我們需要注意375f這個引數,針對這個引數,我們來好好說說。

375這個值是一個UI圖的參照值,單位是dp,我參照的是1334*750畫素的圖

image

然後我們來看下px與dp的轉換圖

Android 單位:dp
MDPI 1px=1dp
HDPI 1.5px=1dp
XHDPI 2px=1dp
XXHDPI 3px=1dp
XXXHDPI 4px=1dp

因為我們UI使用的是藍湖的UI設計,可以根據這些不同的DPI來切換顯示不同佈局的不同寬度dp值

然後我們繼續看那個引數值375,因為我參照的是XHDPI的圖進行繪製的,2px=1dp,375這個值就是寬度的畫素750px/2px=375dp

然後我們切換到XHDPI進行看看是不是375dp

image

那麼,我們就可以參照上面這個圖給出的UI進行設定了,我這個地方給了個測試例子,375dp的話,要想能看出是否精準適配的話,我們可以這樣,採用LineaLayout的vertical佈局,靠左邊設定一個寬度為200dp寬度的widget,靠右設定一個175dp寬度的widget,如果他們兩個相交的地方剛好撘上,那麼,這就是一個精準的適配

image

其他幾個不同畫素的就不展示了,我都測試過了,非常完美解決

對了,還有一個問題,有的UI給出的並不是像我們這種dp方式的,有的UI給的單位是px,其實,這也是很好解決的,比如UI給的是1080*1920的圖,那我們就按照XHDPI 2px=1dp的方式來適配,那麼,我們設定setDensity的值就為1080/2=540dp,然後UI凡是標註多少px,我們就在佈局中給這個px除以2,然後設定到佈局上即可

注意:我們在做dp與px換算的時候,儘量保持這個結果dp值在320~560之間,為什麼呢?看下面表述

我們看上面的換算表格MDPI 1px=1dp,既然1px=1dp,那麼我們怎麼不直接給setDensity設定750呢,這樣,我還省去了計算,直接參照MDPI的圖來做,好,我們就這麼來一次,給大家看看效果,最後再來說原因。

參照是750*1334的UI,setDensity的值也設定750,然後設定左widget的寬度為400dp,右邊的widget設定375dp,看圖

image

確實能寬度適配,但大家看看,這個字型和頁面明顯有縮小的感覺,這種適配雖然寬度達到了,但明顯不是我們想要的,那是什麼原因導致的呢?這時候,我們就要引出一個關鍵因素畫素密度dpi,我來列出兩個公式

dp=px/dpi*160

dpi=px*160/dp

這個計算公式是系統通過我們給定xml的值來計算出顯示在螢幕上的佈局,附錄的程式碼就是利用的這個公式給系統設定計算引數,然後我們把剛剛MDPI適配的方式計算一下dpi

750*160/750=160dpi

160這個密度值明顯過低,導致1密度下面佔據太多的畫素,介面看起來很擁擠,然後我們看下XHDPI的值,750的px等於375dp

750*160/375=320dpi

dpi的值是系統給的,大概查閱了流行的機子的dpi,就是我上面列舉的範圍內,在我們知道畫素寬度的時候,儘量參考Xhdpi來做,這也是我們在適配圖片的時候,喜歡參照xhdpi的原因

其實,當我說到這的時候,原因其實差不多也都說出來了,雖然dpi是系統給的,但我們可以通過getDisplayMetrics替換掉這個dpi,上面的公式也給了,我們可以通過getDisplayMetrics拿到widthPixels螢幕的寬度畫素值,然後再加上我們通過換算拿到的dp值,那dpi的值也就可以知道了,然後將這些值都設定到系統裡面,讓系統在解析xml佈局的時候,按照我們給他設定的值來計算,然後顯示在介面上,這就是適配的原理。


有人說,我是老專案了,如果按照動態這麼適配的話,那豈不之前的都要改?確實,但不要擔心,還有另一種適配方式,smallWidth適配,修改系統的值屬於動態方式來做,那麼sw適配方式就屬於靜態適配了,sw也是根據上面的公式,計算出對應的dp值生成xml,生成xml的java程式碼也有,是一個哥們做的,大家可以看看他的文章,看這,也挺不錯的,計算方式需要自己知道自己適配的機型的dpi,然後通過公式計算出dp值,然後將dp值設定到java程式碼中,然後執行一下,就可以了,將生成的values放到res資料夾下面就可以了

附錄

Application裡面呼叫

    @Override
    public void onCreate() {
     //記住,這個值需要自己根據UI圖計算的哦,可不要直接抄
     Density.setDensity(this, 360);
    }

Density.java

public class Density {
    private static float appDensity;
    private static float appScaledDensity;
    private static DisplayMetrics appDisplayMetrics;
    /**
     * 用來參照的的width
     */
    private static float WIDTH;

    public static void setDensity(@NonNull final Application application, float width) {
        appDisplayMetrics = application.getResources().getDisplayMetrics();
        WIDTH = width;
        registerActivityLifecycleCallbacks(application);

        if (appDensity == 0) {
            //初始化的時候賦值
            appDensity = appDisplayMetrics.density;
            appScaledDensity = appDisplayMetrics.scaledDensity;

            //新增字型變化的監聽
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    //字型改變後,將appScaledDensity重新賦值
                    if (newConfig != null && newConfig.fontScale > 0) {
                        appScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {
                }
            });
        }
    }


    private static void setDefault(Activity activity) {
        setAppOrientation(activity);
    }

    private static void setAppOrientation(@Nullable Activity activity) {

        float targetDensity = 0;
        try {
            targetDensity = appDisplayMetrics.widthPixels / WIDTH;
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

        float targetScaledDensity = targetDensity * (appScaledDensity / appDensity);
        int targetDensityDpi = (int) (160 * targetDensity);

        /**
         *
         * 最後在這裡將修改過後的值賦給系統引數
         *
         * 只修改Activity的density值
         */

        DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
        activityDisplayMetrics.density = targetDensity;
        activityDisplayMetrics.scaledDensity = targetScaledDensity;
        activityDisplayMetrics.densityDpi = targetDensityDpi;
    }


    private static void registerActivityLifecycleCallbacks(Application application) {
        application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                setDefault(activity);
            }
            @Override
            public void onActivityStarted(Activity activity) {
            }
            @Override
            public void onActivityResumed(Activity activity) {
            }
            @Override
            public void onActivityPaused(Activity activity) {
            }
            @Override
            public void onActivityStopped(Activity activity) {
            }
            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }
            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}