修改系统density、densityDpi适配

一、简述

修改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) {

            }
        });

    }
}
发布了201 篇原创文章 · 获赞 249 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_36299025/article/details/104932147