文章目录
一、简述
修改density(屏幕密度,若每英寸有160像素点,该值就为1)、scaleDensity(字体缩放比例),densityDpi(屏幕上每英寸有多少像素点,如160)的值,就是修改系统内部对于目标尺寸而言的像素密度。
二、追根溯源
每个控件的显示,dp、sp等最终都会转换成px为单位,原因在于android源码TypedValue#applyDimension:
public static float applyDimension(int unit, float value,
DisplayMetrics metrics)
{
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}
传入的参数分别是:单位、值、屏幕显示的DisplayMetrics 对象。通过上面的源码可以看出,不管传入的是什么单位,最终都会被转换成px。所以,如果要修改控件的尺寸,只需要修改metrics.density或metrics.scaledDensity即可。修改density和自定义view适配中的scaleX、scaleY类似,不同的设备下的density也不同,同一个分辨率下的density也有可能不一样。所以要对density进行调整,使其跟随分辨率来进行变换。
假设目标设备的像素密度为160,运行设备的像素密度为320,这样算出来的dp值就是320/160=2。既然要修改,就要给出参考像素密度。
三、实现
3.1 定义Density用于对系统类值的修改
定义静态方法setDensity,需要传入application和activity。获取当前app的屏幕显示信息:
DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
定义成员变量表示屏幕密度、字体缩放比例:
private static float appDensity;//表示屏幕密度
private static float appScaleDensity; //字体缩放比例,默认appDensity
if判断赋值:
appDensity = displayMetrics.density;
appScaleDensity = displayMetrics.scaledDensity;
随后计算目标值density, scaleDensity, densityDpi:
float targetDensity = displayMetrics.widthPixels / WIDTH; //比如 1080 / 360 = 3.0
float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);
int targetDensityDpi = (int) (targetDensity * 160);
替换Activity的density, scaleDensity, densityDpi:
DisplayMetrics dm = activity.getResources().getDisplayMetrics();
dm.density = targetDensity;
dm.scaledDensity = targetScaleDensity;
dm.densityDpi = targetDensityDpi;
3.2 在MainActivity中调用
必须在setContentView方法前调用:Density.setDensity(getApplication(),this);这样,参考设备(UI设计图)的宽为320dp,在xml中写160dp就表示显示为宽度的一半,并且在任何设备上都会显示为屏幕的一半。
3.3 处理应用程序关于字体大小的监听
手机设置中设置字体显示大小,必须对其回调进行处理,否则改变配置不会适配文字大小。
//添加字体变化监听回调
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
//字体发生更改,重新对scaleDensity进行赋值
if (newConfig != null && newConfig.fontScale > 0){
appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
//低内存情况,暂不用考虑
}
});
3.4 如果多界面统一处理怎么办?
3.4.1 BaseActivity
BaseActivity中调用Density.setDensity(getApplication(),this);
3.4.2 监听应用程序的变化
Application的onCreate方法中注册监听:
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks()
这里每当一个Activity启动,都会去回调对应的方法,在对应的回调方法中调用Density.setDensity(App.this, activity);即可
四、完整代码
Density.java
public class Density {
private static final float WIDTH = 320;//参考设备的宽,单位是dp 320 / 2 = 160
private static float appDensity;//表示屏幕密度
private static float appScaleDensity; //字体缩放比例,默认appDensity
public static void setDensity(final Application application, Activity activity){
//获取当前app的屏幕显示信息
DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
if (appDensity == 0){
//初始化赋值操作
appDensity = displayMetrics.density;
appScaleDensity = displayMetrics.scaledDensity;
//添加字体变化监听回调
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
//字体发生更改,重新对scaleDensity进行赋值
if (newConfig != null && newConfig.fontScale > 0){
appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
//低内存情况,暂不用考虑
}
});
}
//计算目标值density, scaleDensity, densityDpi
float targetDensity = displayMetrics.widthPixels / WIDTH; // 1080 / 360 = 3.0
float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);
int targetDensityDpi = (int) (targetDensity * 160);
//替换Activity的density, scaleDensity, densityDpi
DisplayMetrics dm = activity.getResources().getDisplayMetrics();
dm.density = targetDensity;
dm.scaledDensity = targetScaleDensity;
dm.densityDpi = targetDensityDpi;
}
}
App.java
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Density.setDensity(App.this, 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) {
}
});
}
}