在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) {
}
});
}