【Android 屏幕适配】屏幕适配通用解决方案 ③ ( 自定义组件解决方案 | 获取设备状态栏高度 | 获取设备屏幕数据 )

2023-03-30 16:14:05 浏览数 (1)

文章目录

  • 一、获取设备状态栏高度
  • 二、获取设备屏幕数据

参考文档 :

  • 设备兼容性概览
  • 屏幕兼容性概览
  • 支持不同的像素密度
  • 声明受限屏幕支持

上一篇博客 【Android 屏幕适配】屏幕适配通用解决方案 ② ( 自定义组件解决方案 | 需要解决的问题 : 设计稿坐标数据转为屏幕真实坐标数据 | 实现步骤 ) 中 , 提出 如果要实现将 宽高为 720 x 1232 的设计稿 , 对应 手机屏幕中除 状态栏之外的 布局 , 需要完成如下操作 :

首先 , 要 获取到实际的设备屏幕数据 , 如 手机屏幕实际宽高 , 屏幕像素密度 DPI 等数据 ;

然后 , 计算实际设备的宽高 , 扣掉状态栏的高度 , 不同手机设备状态栏高度不同 , 然后再进行后续计算 ;

再后 , 给出一个 设计稿 与 屏幕实际有效像素值 的 换算比例 ;

最后 , 根据给出的比例 , 在 自定义组件的 onMeasure 方法 中 , 进 行动态换算 , 计算出在当前设备中每个组件的 实际坐标数据 ;

本篇博客中完成前两项工作 ;

一、获取设备状态栏高度


在 com.android.internal.R$dimen.class 字节码类中 , 封装了 设备屏幕尺寸相关属性 , 在这里我们需要获取该字节码类中的 system_bar_height 属性值 ;

首先 , 通过反射获取 com.android.internal.R$dimen 字节码对象 ;

代码语言:javascript复制
            // 反射 com.android.internal.R$dimen 类
            Class<?> clazz = Class.forName("com.android.internal.R$dimen");

然后 , 调用 Class # newInstance 方法 , 创建 com.android.internal.R$dimen 实例对象 ;

代码语言:javascript复制
            // 创建 com.android.internal.R$dimen 实例对象
            Object instance = clazz.newInstance();

再后 , 获取上述实例对象的 system_bar_height 字段的值 , 该字段的值是一个 int 类型的 资源 ID ;

代码语言:javascript复制
            // 获取指定的字段, 这里用于获取 system_bar_height, 也就是系统状态栏高度
            Field field = clazz.getField(systemid);
            // 获取字段的值
            int fieldValue = (int) field.get(instance);

最后 , 调用 Context # getResources().getDimensionPixelOffset 方法 , 将 dimen 类型的资源 ID 转为实际的像素值 ;

代码语言:javascript复制
            // 获取的字段值是资源 ID, 需要转为实际的像素值
            return context.getResources().getDimensionPixelOffset(fieldValue);

完整代码 :

代码语言:javascript复制
    /**
     * 通过反射 com.android.internal.R$dimen 类, 获取其中的某些字段
     * @param context   上下文对象
     * @param defValue  如果没有成功获取指定字段, 这里返回一个默认值
     * @return
     */
    public int getDimenValue(Context context, int defValue) {
        try {
            // 反射 com.android.internal.R$dimen 类
            Class<?> clazz = Class.forName("com.android.internal.R$dimen");
            // 创建 com.android.internal.R$dimen 实例对象
            Object instance = clazz.newInstance();
            // 获取指定的字段, 这里用于获取 system_bar_height, 也就是系统状态栏高度
            Field field = clazz.getField("system_bar_height");
            // 获取字段的值
            int fieldValue = (int) field.get(instance);
            // 获取的字段值是资源 ID, 需要转为实际的像素值
            return context.getResources().getDimensionPixelOffset(fieldValue);
        } catch (Exception e) {
            // 如果执行出现异常, 则返回默认值
            return defValue;
        }
    }

二、获取设备屏幕数据


获取设备屏幕数据 :

首先 , 获取 WindowManager 实例对象 , 通过调用 Context # getSystemService(Context.WINDOW_SERVICE) 方法 获取 Android 系统服务进行获取该实例 ;

代码语言:javascript复制
        // 获取当前设备的屏幕信息
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);

然后 , 通过 WindowManager 实例获取 DisplayMetrics 示例对象 , 其中封装了设备真实的屏幕数据参数 ; 通过 DisplayMetrics # heightPixels 可以获取屏幕高度 , 通过 DisplayMetrics # widthPixels 可以获取屏幕宽度 ;

代码语言:javascript复制
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);

再后 , 获取手机状态栏高度 ;

代码语言:javascript复制
            // 获取状态框高度
            int statusBarHeight = getDimenValue(context,48);

最后 , 处理状态栏信息 , 如果是横屏 , 在宽度方向上减去状态栏高度 , 如果是竖屏 , 在高度上减去状态栏高度 ; 这里通过对比屏幕的宽高来判定当前是横屏还是竖屏 ;

代码语言:javascript复制
            // 在屏幕真实宽高上减去状态栏高度
            if(displayMetrics.widthPixels > displayMetrics.heightPixels){
                // 宽度大于高度说明是横屏状态, 状态栏在左侧或者右侧
                this.screenWidth = displayMetrics.heightPixels;
                this.screenHeight = displayMetrics.widthPixels - statusBarHeight;
            }else{
                // 高度大于宽度说明是竖屏状态, 状态栏在上侧
                this.screenWidth = displayMetrics.widthPixels;
                this.screenHeight = displayMetrics.heightPixels - statusBarHeight;
            }

完整代码如下 :

代码语言:javascript复制
    private void initMetrics(){
        // 获取当前设备的屏幕信息
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);

        if(screenWidth == 0.0f || screenHeight == 0.0f){
            //获取状态框信息
            int statusBarHeight = getDimenValue(context,48);

            // 在屏幕真实宽高上减去状态栏高度
            if(displayMetrics.widthPixels > displayMetrics.heightPixels){
                // 宽度大于高度说明是横屏状态, 状态栏在左侧或者右侧
                this.screenWidth = displayMetrics.heightPixels;
                this.screenHeight = displayMetrics.widthPixels - statusBarHeight;
            }else{
                // 高度大于宽度说明是竖屏状态, 状态栏在上侧
                this.screenWidth = displayMetrics.widthPixels;
                this.screenHeight = displayMetrics.heightPixels - statusBarHeight;
            }
        }
    }

0 人点赞