getDimension、getDimensionPixelOffset、getDimensionPixelSize 源码解析之三者区别

在做自定义控件的时候,通过TypeArray获取控件的margin或padding值的时候,出现了getDimension、 getDimensionPixelOffset 、getDimensionPixelSize三种方法获取,这三者都有什么区别,看源码

源码分析

getDimension方法源码

public float getDimension(@StyleableRes int index, float defValue) {
        if (mRecycled) {
            throw new RuntimeException("Cannot make calls to a recycled instance!");
        }

        //不用管,因为三个方法都有同样的代码
        final int attrIndex = index;
        index *= AssetManager.STYLE_NUM_ENTRIES;

        final int[] data = mData;
        final int type = data[index+AssetManager.STYLE_TYPE];
        if (type == TypedValue.TYPE_NULL) {//为空时,走默认值
            return defValue;
        } else if (type == TypedValue.TYPE_DIMENSION) {//自己指定了值走此方法
            return TypedValue.complexToDimension(
                    data[index + AssetManager.STYLE_DATA], mMetrics);
        } else if (type == TypedValue.TYPE_ATTRIBUTE) {//走异常
            final TypedValue value = mValue;
            getValueAt(index, value);
            throw new UnsupportedOperationException(
                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
        }
        //走异常
        throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
                + " to dimension: type=0x" + Integer.toHexString(type));
    }

getDimensionPixelOffset源码

public int getDimensionPixelOffset(@StyleableRes int index, int defValue) {
        if (mRecycled) {
            throw new RuntimeException("Cannot make calls to a recycled instance!");
        }
        //不用管,因为三个方法都有同样的代码
        final int attrIndex = index;
        index *= AssetManager.STYLE_NUM_ENTRIES;

        final int[] data = mData;
        final int type = data[index+AssetManager.STYLE_TYPE];
        if (type == TypedValue.TYPE_NULL) {//为空时,走默认值
            return defValue;
        } else if (type == TypedValue.TYPE_DIMENSION) {//自己指定了值走此方法
            return TypedValue.complexToDimensionPixelOffset(
                    data[index + AssetManager.STYLE_DATA], mMetrics);
        } else if (type == TypedValue.TYPE_ATTRIBUTE) {//走异常
            final TypedValue value = mValue;
            getValueAt(index, value);
            throw new UnsupportedOperationException(
                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
        }

        throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
                + " to dimension: type=0x" + Integer.toHexString(type));
    }

getDimensionPixelSize源码

public int getDimensionPixelSize(@StyleableRes int index, int defValue) {
        if (mRecycled) {
            throw new RuntimeException("Cannot make calls to a recycled instance!");
        }
        //不用管,因为三个方法都有同样的代码
        final int attrIndex = index;
        index *= AssetManager.STYLE_NUM_ENTRIES;

        final int[] data = mData;
        final int type = data[index+AssetManager.STYLE_TYPE];
        if (type == TypedValue.TYPE_NULL) {//为空时,走默认值
            return defValue;
        } else if (type == TypedValue.TYPE_DIMENSION) {//自己指定了值走此方法
            return TypedValue.complexToDimensionPixelSize(
                data[index+AssetManager.STYLE_DATA], mMetrics);
        } else if (type == TypedValue.TYPE_ATTRIBUTE) {
            final TypedValue value = mValue;
            getValueAt(index, value);
            throw new UnsupportedOperationException(
                    "Failed to resolve attribute at index " + attrIndex + ": " + value);
        }

        throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
                + " to dimension: type=0x" + Integer.toHexString(type));
    }

当我们指定了具体值的情况下,会给我们返回

getDimension返回float值

return TypedValue.complexToDimension(
       data[index + AssetManager.STYLE_DATA], mMetrics);

getDimensionPixelOffset返回int值

return TypedValue.complexToDimensionPixelOffset(
       data[index + AssetManager.STYLE_DATA], mMetrics);

getDimensionPixelSize返回int值

return TypedValue.complexToDimensionPixelSize(
       data[index+AssetManager.STYLE_DATA], mMetrics);

getDimension返回是float,getDimensionPixelOffset、getDimensionPixelSize返回int

TypedValue调用的方法传递的参数都是相同的,再跟进去看下

getDimension的方法
public static float complexToDimension(int data, DisplayMetrics metrics)
    {
        return applyDimension(
            //转换成px、dp、px单位对应得值
            (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
            complexToFloat(data),
            //用于获取屏幕密度
            metrics);
    }

getDimensionPixelOffset方法
public static int complexToDimensionPixelOffset(int data,
            DisplayMetrics metrics)
    {
        //返回和getDimension的一样,只是getDimension返回的float值,这里强转成int值
        return (int)applyDimension(
                (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
                complexToFloat(data),
                metrics);
    }

getDimensionPixelSize方法
public static int complexToDimensionPixelSize(int data,
            DisplayMetrics metrics)
    {
        //在getDimension、getDimensionPixelOffset方法里一样,都有此方法,只不过这里是单独写了,便于四舍五入
        final float value = complexToFloat(data);
        //和前两个都一样
        final float f = applyDimension(
                (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,
                value,
                metrics);
        //这里做了四舍五入
        final int res = (int) ((f >= 0) ? (f + 0.5f) : (f - 0.5f));
        if (res != 0) return res;
        //单独把complexToFloat方法抽离出来,目的就是当res是0时用
        if (value == 0) return 0;
        if (value > 0) return 1;
        return -1;
    } 

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;
    }

当给定值是px,则直接返回给定的值
当给定值是dp,则需乘以屏幕密度再返回
当给定值是sp,则需乘以缩放因子再返回

注意:一般情况下(不修改系统字体大小),metrics.density和metrics.scaledDensity是一个值。

测试

在values->attrs.xml下定义

<declare-styleable name="CustomView">
        <attr name="layout_marginTop" format="dimension"/>
        <attr name="layout_marginRight" format="dimension"/>
        <attr name="layout_marginBottom" format="dimension"/>
</declare-styleable>

在布局中定义

<com.cn.liuyz.customviewdemo.CustomView
        android:id="@+id/cv"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        custom:layout_marginTop="8.5dp"
        custom:layout_marginRight="8.5px"
        custom:layout_marginBottom="8.5sp"/>

在自定义View中定义

private void initView(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView, 0, 0);

        //分别获取getDimension、getDimensionPixelOffset、getDimensionPixelSize值
        float top1 = typedArray.getDimension(R.styleable.CustomView_layout_marginTop, 0f);//dp
        int top2 = typedArray.getDimensionPixelOffset(R.styleable.CustomView_layout_marginTop, 0);//dp
        int top3 = typedArray.getDimensionPixelSize(R.styleable.CustomView_layout_marginTop, 0);//dp

        float right1 = typedArray.getDimension(R.styleable.CustomView_layout_marginRight, 0f);//px
        int right2 = typedArray.getDimensionPixelOffset(R.styleable.CustomView_layout_marginRight, 0);//px
        int right3 = typedArray.getDimensionPixelSize(R.styleable.CustomView_layout_marginRight, 0);//px

        float bottom1 = typedArray.getDimension(R.styleable.CustomView_layout_marginBottom, 0f);//sp
        int bottom2 = typedArray.getDimensionPixelOffset(R.styleable.CustomView_layout_marginBottom, 0);//sp
        int bottom3 = typedArray.getDimensionPixelSize(R.styleable.CustomView_layout_marginBottom, 0);//sp
        typedArray.recycle();

        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        float density =  metrics.density;
        float scaledDensity = metrics.scaledDensity;

        Log.d("liuyz:", "density=" + density + ", scaledDensity=" + scaledDensity);
        Log.d("liuyz:top", "getDimension= "+top1+", getDimensionPixelOffset="+top2+",getDimensionPixelSize="+top3);
        Log.d("liuyz:right", "getDimension= "+right1+", getDimensionPixelOffset="+right2+",getDimensionPixelSize="+right3);
        Log.d("liuyz:bottom", "getDimension= "+bottom1+", getDimensionPixelOffset="+bottom2+",getDimensionPixelSize="+bottom3);
    }

在布局中传入marginTop=8.5dp、marginRight=8.5px、marginBottom=8.5sp 打印结果如下:

com.cn.liuyz.customviewdemo D/liuyz:: density=2.75, scaledDensity=2.75
com.cn.liuyz.customviewdemo D/liuyz:top: getDimension= 23.375, getDimensionPixelOffset=23,getDimensionPixelSize=23
com.cn.liuyz.customviewdemo D/liuyz:right: getDimension= 8.5, getDimensionPixelOffset=8,getDimensionPixelSize=9
com.cn.liuyz.customviewdemo D/liuyz:bottom: getDimension= 23.375, getDimensionPixelOffset=23,getDimensionPixelSize=23

由打印结果得知

  • top值给定的是dp,getDimension得到是float值,getDimensionPixelOffset是把float强制成int值,getDimensionPixelSize是四舍五入的float值
  • right给定的px,安装top值得出的规则,getDimension= 8.5,getDimensionPixelOffset强转成int值是8,getDimensionPixelSize四舍五入值为9

总结

不同点

  1. getDimension 返回float值
  2. getDimensionPixelOffset 将float强转为int值返回
  3. getDimensionPixelSize 将float值四舍五入成int值返回

相同点

  • 当给定值单位为px,给定值直接返回
  • 当给定值单位为dp或px,给定值乘以屏幕密度后返回
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值