轉載請做明出處:juejin.im/post/5b3094…android
做爲一個Android開發人員,你還在爲了適配各類尺寸的屏幕而苦惱嗎?你還在爲了出現一個新的機型而修改着數不盡的dimens和layout嗎?你還在爲了UI給的奇葩尺寸的設計圖而絞盡奶汁計算距離嗎?若是你爲了這些事情而苦惱,那麼看完這篇文章,但願能夠幫你減小開發時間,減緩生命的流逝速度。。。 git
不知道你們有沒有看過前一段時間今日頭條技術團隊發表的一篇關於Android屏幕適配的文章: 一種極低成本的Android屏幕適配方式。沒有看過的朋友能夠先看看了解一下再回來,能夠更好的理解。我是無心中點開的這篇文章,可是看過以後眼前一亮-------Android屏幕適配要是真的這麼簡單,那些辛辛苦苦沒日沒夜作適配的前輩們是否是死得太慘了。。。。。。。不得不說今日頭條的大神們的想法真的很是獨到,成本極其低廉,還特別好用。他們給出的最終方案是這樣的:github
private static float sRoncompatDennsity;
private static float sRoncompatScaledDensity;
private void setCustomDensity(@NonNull Activity activity, final @NonNull Application application) {
//application
final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
if (sRoncompatDennsity == 0) {
sRoncompatDennsity = appDisplayMetrics.density;
sRoncompatScaledDensity = appDisplayMetrics.scaledDensity;
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig != null && newConfig.fontScale > 0) {
sRoncompatScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
//計算寬爲360dp 同理能夠設置高爲640dp的根據實際狀況
final float targetDensity = appDisplayMetrics.widthPixels / 360;
final float targetScaledDensity = targetDensity * (sRoncompatScaledDensity / sRoncompatDennsity);
final int targetDensityDpi = (int) (targetDensity * 160);
appDisplayMetrics.density = targetDensity;
appDisplayMetrics.densityDpi = targetDensityDpi;
appDisplayMetrics.scaledDensity = targetScaledDensity;
//activity
final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
}
複製代碼
看到這篇文章以後我趕忙就寫了一個demo測試了一下,發現了一點小問題。 咱們UI給出的設計圖尺寸爲1334*720,若是我以寬度做爲適配標準的話,按照xhdpi 1dp=2px 的圖進行參照,設計圖寬720px的,屏幕的寬度應爲360dp,也就是這樣:bash
final float targetDensity = appDisplayMetrics.widthPixels / 360;
複製代碼
這樣作的話寬度適配的比例是沒有任何問的,可是我在想,若是某一個頁面須要以高度來作適配(也就是內容恰好縱向填充全屏)的話,是否是改爲這樣就能夠了:app
final float targetDensity = appDisplayMetrics.heightPixels / 667;
複製代碼
可是運行以後發現,高度上的差別很大,運行在不一樣分辨率和尺寸的手機上,頁面中的每一部份內容在縱向上的比例不盡相同,沒有達到很好的適配的效果。 思考了許久事後我發現一個問題:我手邊的測試機的寬度是兩個720和兩個1080,而高度有1280,1440,1780和一個全面屏的2160。Android的開原性致使了Android設備的尺寸的碎片化太嚴重,而經過查看測試機的尺寸參數會發現,若是用這四個手機來測試的話,寬度能夠直接整除,而高度不能夠(而且我手邊的測試機的寬度也能夠整除,若是有寬度無法整除的手機呢?)。可是用今日頭條給出的方法,作除法後結果會取整,那會不會是因爲用縱向計算出來的density取整影響了精度,從而致使了效果不盡人意呢?ide
發現上述問題以後我就着手去修改,將計算結果取餘後在賦值給targetDensity,通過反覆的測試與實驗,我從新修改了targetDensity的計算方法:佈局
float targetDensity = 0;
try {
Double division = Operation.division(appDisplayMetrics.heightPixels, 667);
//因爲手機的長寬不盡相同,確定會有除不盡的狀況,有失精度,因此在這裏把所得結果作了一個保留兩位小數的操做
DecimalFormat df = new DecimalFormat("0.00");
String s = df.format(division);
targetDensity = Float.parseFloat(s);
} catch (NumberFormatException e) {
e.printStackTrace();
}
複製代碼
可是有熱心的網友給我留言,說若是把系統的語言改爲葡萄牙語以後會報異常,頁面徹底就是慘不忍睹。。。抱着對外國曆史友人的疑問我繼續去測試,發現了這樣的問題:post
仔細看!!!小數點居然是中文逗號(目前發現的語言當中,葡語和印尼語是這樣,其餘語言未經測試)!!!沒辦法,只能繼續修改了,後來通過這位熱心網友提醒,我將這部分代碼修改爲:float targetDensity = appDisplayMetrics.heightPixels / 667f;
複製代碼
這樣一來就徹底沒有問題了,也不須要作保留兩位小數處理了(發現問題的朋友們,原諒我學藝不精。。。也感謝這位熱心網友的指正)測試
繼續測試後發現,高度上的適配結果讓人很是滿意。但是還有一個問題,咱們通常來講作適配都是以手機的寬度爲基準,可是一個app裏面避免不了偶爾一兩個頁面是按照高度爲基準(就是內容縱向填充全屏的頁面)作適配的。可是上述方法只能保證一個方向,那我就讓它能夠自由的切換適配的基準方向不就行了。字體
繼續修改以後我獲得了最終的方案,修改事後這個類中的全部內容以下:
private static float appDensity;
private static float appScaledDensity;
private static DisplayMetrics appDisplayMetrics;
private static int barHeight;
public static void setDensity(@NonNull Application application) {
//獲取application的DisplayMetrics
appDisplayMetrics = application.getResources().getDisplayMetrics();
//獲取狀態欄高度
barHeight = AppUtils.getStatusBarHeight(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() {
}
});
}
}
//此方法在BaseActivity中作初始化(若是不封裝BaseActivity的話,直接用下面那個方法就行了)
public static void setDefault(Activity activity) {
setAppOrientation(activity, AppUtils.WIDTH);
}
//此方法用於在某一個Activity裏面更改適配的方向
public static void setOrientation(Activity activity, String orientation) {
setAppOrientation(activity, orientation);
}
/**
* targetDensity
* targetScaledDensity
* targetDensityDpi
* 這三個參數是統一修改事後的值
* <p>
* orientation:方向值,傳入width或height
*/
private static void setAppOrientation(@Nullable Activity activity, String orientation) {
float targetDensity;
if (orientation.equals("height")) {
targetDensity = (appDisplayMetrics.heightPixels - barHeight) / 667f;
} else {
targetDensity = appDisplayMetrics.widthPixels / 360f;
}
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;
}
複製代碼
在這個類的初始化方法裏面我是默認的以寬度來做爲基準(這是在Activity中設置的方法,存在於此Activity下的fragment,dialog和PopupWindow都會受到此效果的影響,也就是說,在Activity中設置一次以後,Activity下的其餘子View都無需再設置一次)。
本身建立一個類,將最終方案裏面的代碼複製粘貼就可使用了
使用方法:在Application的onCreate()方法中:
在BaseActivity中: 若是隻是適配一個方向的話,只設置這兩句就能夠了(我在utils裏面設置了默認按照寬度適配,能夠根據本身的需求修改默認的適配方向,見下圖) 若app中有某一個頁面須要縱向適配的話:/**
*
* 因爲是我的封裝,此方法須要寫在onCreate()中的setContentView()方法前面,切換方向的效果纔會生效
*/
@Override
public void setOrientation() {
Density.setOrientation(this, AppUtils.HEIGHT);
}
複製代碼
最後貼出適配的效果圖(顏色只是爲了看的直觀一點。。。)
橫向:
縱向:使用此方法,只須要一個dimens文件,一個layout文件就足矣,在xml佈局中直接只用dp就能夠了(Android P的劉海屏須要單獨適配layout)
因爲是本身寫的demo,尚未大面積測試,要是各位看官有條件大範圍測試的話,出現什麼問題能夠反饋給我,咱們能夠一塊兒討論該如何修改,共同進步。 這是我入行以來寫的第一篇文章,有寫的很差的地方歡迎指正,之後還會繼續努力多寫文章的,好的東西須要分享。
這裏是github地址,demo裏面還有BaseRecyclerViewAdapterHelper和QMUI的使用方法
留個聯繫方式,方便溝通 QQ:850701071
這篇文章若是幫到你的話,點個喜歡再走唄