Android getDimension()、getDimensionPixelSize()和getDimensionPixelOffset()的详解

最近这一段时间比较忙,都没怎么写博客了,这篇文章主要想介绍几个有关dp、sp和px相互转换的系统api:getDimension()、getDimensionPixelSize()和getDimensionPixelOffset()的区别。
回想起刚学android时,dp转px或sp转px都是用的网上的工具类DensityUtils,如下:

   /**
     * dp转px
     */
    public static int dp2px(float dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, Resources.getSystem().getDisplayMetrics());
    }

    /**
     * sp转px
     */
    public static int sp2px(float spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, Resources.getSystem().getDisplayMetrics());
    }

    /**
     * px转dp
     */
    public static float px2dp(float pxVal) {
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (pxVal / scale);
    }

    /**
     * px转sp
     */
    public static float px2sp(float pxVal) {
        return (pxVal / Resources.getSystem().getDisplayMetrics().scaledDensity);
    }
    
其实它们的实现逻辑也是一样的,都是根据系统的api来换算出来的,接下来验证一下,我们来引入utilcodex库:

    implementation 'com.blankj:utilcodex:1.30.6'
    
然后在dimens文件定义几个值:

    <dimen name="dp_41">41dp</dimen>
    <dimen name="px_41">41px</dimen>
    <dimen name="sp_41">41sp</dimen>
    
最后在Activity里打印日志验证我们的结果:

        float density = ScreenUtils.getScreenDensity();
        float scaledDensity = Resources.getSystem().getDisplayMetrics().scaledDensity;
        int densityDpi = ScreenUtils.getScreenDensityDpi();
        Log.e(TAG, "density:" + density);
        Log.e(TAG, "scaledDensity:" + scaledDensity);
        Log.e(TAG, "densityDpi:" + densityDpi);

        float floatDp = this.getResources().getDimension(R.dimen.dp_41);
        int pixelSizeDp = this.getResources().getDimensionPixelSize(R.dimen.dp_41);
        int pixelOffsetDp = this.getResources().getDimensionPixelOffset(R.dimen.dp_41);

        Log.e(TAG, "floatDp:" + floatDp);
        Log.e(TAG, "pixelSizeDp:" + pixelSizeDp);
        Log.e(TAG, "pixelOffsetDp:" + pixelOffsetDp);

        float floatPx = this.getResources().getDimension(R.dimen.px_41);
        int pixelSizePx = this.getResources().getDimensionPixelSize(R.dimen.px_41);
        int pixelOffsetPx = this.getResources().getDimensionPixelOffset(R.dimen.px_41);

        Log.e(TAG, "floatPx:" + floatPx);
        Log.e(TAG, "pixelSizePx:" + pixelSizePx);
        Log.e(TAG, "pixelOffsetPx:" + pixelOffsetPx);

        float floatSp = this.getResources().getDimension(R.dimen.sp_41);
        int pixelSizeSp = this.getResources().getDimensionPixelSize(R.dimen.sp_41);
        int pixelOffsetSp = this.getResources().getDimensionPixelOffset(R.dimen.sp_41);

        Log.e(TAG, "floatSp:" + floatSp);
        Log.e(TAG, "pixelSizeSp:" + pixelSizeSp);
        Log.e(TAG, "pixelOffsetSp:" + pixelOffsetSp);

        Log.e(TAG, "sp2px:" + DensityUtil.sp2px(40f));
        Log.e(TAG, "dp2px:" + DensityUtil.dp2px(40f));
        
其中density 是我们测试手机的屏幕密度,scaledDensity是我们手机的缩放密度,而densityDpi 是像素密度,不太了解的可去度娘看看。 日志结果

2021-08-16 16:52:58.943 17590-17590/com.littlejerk.sample E/DpActivity: density:2.75
2021-08-16 16:52:58.943 17590-17590/com.littlejerk.sample E/DpActivity: scaledDensity:2.75
2021-08-16 16:52:58.943 17590-17590/com.littlejerk.sample E/DpActivity: densityDpi:440
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: floatDp:112.75
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelSizeDp:113
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelOffsetDp:112
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: floatPx:41.0
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelSizePx:41
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelOffsetPx:41
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: floatSp:112.75
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelSizeSp:113
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: pixelOffsetSp:112
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: sp2px:110
2021-08-16 16:52:58.944 17590-17590/com.littlejerk.sample E/DpActivity: dp2px:110

从打印结果就可以推知
  1. getDimension()、getDimensionPixelSize和getDimensionPixelOffset方法传入的参数单位不同,换算也不同;对于小数点的精确也不同。
  2. getDimension()方法返回的是float类型,不会对小数点进行额外的操作,getDimensionPixelSize()和getDimensionPixelOffset()方法返回的虽然都是int类型,但是前者会进行四舍五入操作,而后者会直接舍弃小数点后面的值。
  3. 三个方法传入参数的单位不同,它们的换算方式也不同。
  4. 对于dp单位的值,换算方式为density乘以该dp值;对于sp单位的值,换算方式为scaledDensity乘以该sp的值;而对于px来说,没做任何运算,直接等于该px值
可能有人会对sp单位的换算有疑问,为何是scaledDensity而不是density呢?确实是会有这样的问题,因为在测试机上scaledDensity和density的值是一样呀。

其实要想验证这个问题也不难,可以直接去手机设置修改字体的大小,这样scaledDensity和density就不一样了。还有一个方法就是直接看这些方法里面的实现,这样就可以百分百确认我们的推论了,进入这三个方法就可知道,它们最后都调用了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;
    }
看到这就一目了然了吧,这完全说明上面的结论是对的。
还有个问题是为啥工具类换算出来的值相差了一点呢?其实这也是小数点和取整造成的,1px左右的差距倒影响不大,具体使用哪几个方法获取px,看自己喜好了。
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Android UI开发专题 Android UI开发专题(一) 之界面设计 近期很多网友对Android用户界面的设计表示很感兴趣,对于Android UI开发自绘控件和游戏制作而言掌握好绘图基础是必不可少的。本次专题分10节来讲述,有关OpenGL ES相关的可能将放到以后再透露。本次主要涉及以下四个包的相关内容:  android.content.res 资源类   android.graphics 底层图形类   android.view 显示类   android.widget 控件类   一、android.content.res.Resources   对于Android平台的资源类android.content.res.Resources可能很多网友比较陌生,一起来看看SDK上是怎么介绍的吧,Contains classes for accessing application resources, such as raw asset files, colors, drawables, media or other other files in the package, plus important device configuration details (orientation, input types, etc.) that affect how the application may behave.平时用到的二进制源文件raw、颜色colors、图形drawables和多媒体文件media的相关资源均通过该类来管理。   int getColor(int id) 对应res/values/colors.xml   Drawable getDrawable(int id) 对应res/drawable/   XmlResourceParser getLayout(int id) 对应res/layout/   String getString(int id) 和CharSequence getText(int id) 对应res/values/strings.xml   InputStream openRawResource(int id) 对应res/raw/   void parseBundleExtra (String tagName, AttributeSet attrs, Bundle outBundle) 对应res/xml/   String[] getStringArray(int id) res/values/arrays.xml   float getDimension(int id) res/values/dimens.xml

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄小梁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值