AndroidUtilCode AdaptScreenUtils屏幕适配使用笔记及简单分析

燕扬
2023-12-01

头条适配原理
Android 屏幕适配终结者
一种粗暴快速的Android全屏幕适配方案

屏幕适配原理简单说下
  • 尺寸单位介绍
  1. dpi:屏幕密度,屏幕上每英寸上有多少像素点.屏幕对角线上的像素点总数除以屏幕对角线长度得到.
  2. dip:也就是最常用的dp单位,屏幕上每英寸有160个点,每个点如果代表1像素,那么1dp就等于1像素.
    - 问题来了,假如屏幕上每英寸有320个像素,屏幕宽是1080像素,此时该屏幕宽是多少dp?
    1. 屏幕每英寸320像素,如果用160个点去覆盖这些像素,每个点可以代表2个像素,也就是2个像素代表1dp,屏幕此时有1080像素,就需要用540个点覆盖,屏幕大小就是540dp.
  3. density:每1英寸像素点除160,也就是每英寸有160个点,每个点能覆盖多少个像素.
  • 解释原理
  1. UI图尺寸与像素都是固定的.
  2. 如果设计图宽为360dp,不同设备屏幕像素不同,要想360dp乘以density计算出的像素值都等于各个设备屏幕像素宽度,那么可以更改当前Activity中的density来达到目的.
看看AdaptScreenUtils它是如何做的适配
  1. 它使用起来非常的简单,只要重写Activity中的getResources()方法,如下:
@Override
public Resources getResources() {
    // 这里是以高度为基准来适配的
    return AdaptScreenUtils.adaptHeight(super.getResources(), "UI高度像素值");
}
  1. 看看源码
public final class AdaptScreenUtils {
    public static Resources adaptHeight(final Resources resources, final int designHeight) {
        // designHeight代表UI设计稿中的高度像素值
        return adaptHeight(resources, designHeight, false);
    }
    public static Resources adaptHeight(final Resources resources, final int designHeight, final boolean includeNavBar) {
        // 获取屏幕高度像素值
        float screenHeight = (resources.getDisplayMetrics().heightPixels  + (includeNavBar ? getNavBarHeight(resources) : 0)) * 72f;
        // 以屏幕高度像素值除以设计稿中高度像素值,传入applyDisplayMetrics()方法中
        float newXdpi = screenHeight / designHeight;
        applyDisplayMetrics(resources, newXdpi);
        return resources;
    }
    private static void applyDisplayMetrics(final Resources resources, final float newXdpi) {
        // 将上面得到值赋值给DisplayMetrics中的xdpi变量
        resources.getDisplayMetrics().xdpi = newXdpi;
        Utils.getApp().getResources().getDisplayMetrics().xdpi = newXdpi;
        applyOtherDisplayMetrics(resources, newXdpi);
    }
    // 该方法是通过反射为将resources对象中的xdpi变量改为newXdpi值.
    private static void applyOtherDisplayMetrics(final Resources resources, final float newXdpi) {
        if (sMetricsFields == null) {
            sMetricsFields = new ArrayList<>();
            Class resCls = resources.getClass();
            Field[] declaredFields = resCls.getDeclaredFields();
            while (declaredFields != null && declaredFields.length > 0) {
                for (Field field : declaredFields) {
                    if (field.getType().isAssignableFrom(DisplayMetrics.class)) {
                        field.setAccessible(true);
                        DisplayMetrics tmpDm = getMetricsFromField(resources, field);
                        if (tmpDm != null) {
                            sMetricsFields.add(field);
                            tmpDm.xdpi = newXdpi;
                        }
                    }
                }
                // 包括resources父类对象中的xdpi值都要改为newXdpi值
                resCls = resCls.getSuperclass();
                if (resCls != null) {
                    declaredFields = resCls.getDeclaredFields();
                } else {
                    break;
                }
            }
        } else {
            applyMetricsFields(resources, newXdpi);
        }
    }
}

xDpi在哪儿用的上?

public class TypedValue {
    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:
            // 假如使用的是pt作为单位,xdpi为屏幕像素比设计稿像素,再乘以当前value值,最终的结果就是value值在屏幕中所占像素值.后面的我不太懂0.0.
            // 使用pt作为单位可以避开dp单位,这样对原有的项目影响最小.
            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;
    }
}
注意

对于TextView中文字大小,建议使用sp作为单位,这样文字大小可以自动适配.然后千万不要写死TextView尺寸大小,不然很容易出现文字比TextView尺寸还大导致显示不全的现象.建议使用wrap_content.

 类似资料: