螢幕適配方案
使用
直接在Application的onCreate方法中呼叫(Density類直接參照附錄)
Density.setDensity(this, 375f);
這個地方我們需要注意375f這個引數,針對這個引數,我們來好好說說。
375這個值是一個UI圖的參照值,單位是dp,我參照的是1334*750畫素的圖
然後我們來看下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
那麼,我們就可以參照上面這個圖給出的UI進行設定了,我這個地方給了個測試例子,375dp的話,要想能看出是否精準適配的話,我們可以這樣,採用LineaLayout的vertical佈局,靠左邊設定一個寬度為200dp寬度的widget,靠右設定一個175dp寬度的widget,如果他們兩個相交的地方剛好撘上,那麼,這就是一個精準的適配
其他幾個不同畫素的就不展示了,我都測試過了,非常完美解決
對了,還有一個問題,有的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,看圖
確實能寬度適配,但大家看看,這個字型和頁面明顯有縮小的感覺,這種適配雖然寬度達到了,但明顯不是我們想要的,那是什麼原因導致的呢?這時候,我們就要引出一個關鍵因素畫素密度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) {
}
});
}
}