在Android開發中,由於Android碎片化嚴重,屏幕分辨率千奇百怪,而想要在各種分辨率的設備上顯示基本一致的效果,適配成本越來越高。雖然Android官方提供了dp單位來適配,但其在各種奇怪分辨率下表現卻不盡如人意,因此今日頭條適配就出來了,適配極其簡單,主要是一些原理。
android中的dp在渲染前會將dp轉爲px,計算公式:
px = density * dp;
density = dpi / 160;
px = dp * (dpi / 160);
而dpi是根據屏幕真實的分辨率和尺寸來計算的,每個設備都可能不一樣的,因爲安卓的碎片化太嚴重導致的。
廢話就不多說了,直接上代碼
假如UI設計師給的是寬度爲360dp的設計稿
/**
* 概念性:
* 1、什麼是px(分辨率)爲什麼不能用px?爲什麼有dp單位?爲什麼dp不能適配所有屏幕?到最後的屏幕適配的解決方案
* 2、什麼是屏幕尺寸(英寸)
* 3、什麼是屏幕密度dpi(每英寸有多少像素點)
* 4、什麼是密度(爲了適配谷歌自定義的密度)
* 5、什麼是dp(與屏幕密度無關的計量單位)
*
* 今日頭條的解決方案?
* 假如固定設計圖的寬或者高 單位是dp 360dp
*
* px = 360dp * 密度 每種手機的分辨率不一樣:假如現在有這幾種720px 1080px 1440px * 2048px
*
* 360 = px / 密度
*
* 密度 = px / 360
*
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 100dp 的按鈕在所有屏幕上佔的比例是一樣的 屏幕的分辨率是px值
//原始比例 是不是 0.27777
//不同手機的分辨率:720px 1080px 1440px 2048px
//不同手機分辨率對應的密度:2,3,4,5.689
//在不同設備上100dp對應的px值:200px 300px 400px 569px 0.277
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int widthPx = displayMetrics.widthPixels;//得到屏幕寬的分辨率
int heightPx = displayMetrics.heightPixels;//得到屏幕高的分辨率
float density = displayMetrics.density;//密度
float scaledDensity = displayMetrics.scaledDensity;//與字體大小相關的密度
//原始的scaledDensity 比上 原始的density
float a = scaledDensity / density; //計算變化後的比例 原始系統的比例
float newDensity = widthPx / 360f;//真實的密度 360是dp的值
float newscaledDensity = newDensity * a;
displayMetrics.density = newDensity;//決定佈局尺寸
displayMetrics.scaledDensity = newscaledDensity;//決定字體大小
setContentView(R.layout.activity_main);
// 360dp 設計圖寬度
TextView textView = findViewById(R.id.showResult);
textView.setText("widthPx = "+widthPx+",heightPx = "+heightPx+",density = "+density+",newDensity="+newDensity);
Log.i("abc","widthPx = "+widthPx+",heightPx = "+heightPx+",density = "+density);
}
}
然由於電腦原因展示不了效果各位請自行運行下載各個手機上測試
由於我是按照UI設計稿爲360dp來計算的各位可以先複製我的測試下,至於佈局文件在下面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:background="#fff0"
android:id="@+id/showResult"
android:layout_width="180dp"
android:layout_height="wrap_content"
android:text="Hello World!"
/>
</LinearLayout>
各位自行測試,如果可以 ,假如你項目用的是MVP的話就把這個複製到BaseActivity中zhuy注意要在setContentView()之前設置
至於360dp是我的測試,你們可以按照zi自己的UI圖來設置寬度
//下面這個是封裝好的適配工具類只需調用即可
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) {
}
});
}