从源码看getDimension()、getDimensionPixelOffset()以及getDimensionPixelSize()三个区别

本文详细解析了Android开发中TypeArray的getDimension、getDimensionPixelOffset和getDimensionPixelSize三个方法的源码及区别。通过源码分析和简单验证,总结出这三个方法的主要差异在于返回值类型:getDimension返回float,getDimensionPixelOffset强转为int,getDimensionPixelSize则是四舍五入后的int值。

     一、前言

我们在自定义控件的时候,通过 TypeArray(其实Resources 类也有这个方法,等等说。)的 getDimension、 getDimensionPixelSize 、getDimensionPixelOffset 可以获取到尺寸,然后,懵逼了。

     二、源码

2.1 下面会分别贴出 TypeArray 中的三个方法的源码,加了简单的中文翻译,稍微扫一眼就ok了,看看我的翻译就先过去

2.2  getDimension  的源码,保留了源码中的原始说明,毕竟我翻译的很low,自己看不下去但是又翻译不好。

    /**
     * Retrieve a dimensional unit attribute at <var>index</var>. Unit
     * conversions are based on the current {@link DisplayMetrics}
     * associated with the resources this {@link TypedArray} object
     * came from.
     *
     * 通过指定的 参数inde 获得一个尺寸的单位属性。该单位转换是基于你当前机器屏幕的DisplayMetrics
     * 以及和当前TypedArray 的对象持有的资源而得到的,
     * <p>
     * This method will throw an exception if the attribute is defined but is
     * not a dimension.
     * 当属性不是尺寸的时候,即使确实被定义了,也会抛出一个异常的。
     * @param index Index of attribute to retrieve.
     * @param defValue Value to return if the attribute is not defined or
     *                 not a resource.
     *
     * @return Attribute dimension value multiplied by the appropriate
     *         metric, or defValue if not defined.
     *         结果将被乘上一个合适的 metric
     * @throws RuntimeException if the TypedArray has already been recycled.
     *          如果当前TypedArray 被recycle 会抛异常
     * @throws UnsupportedOperationException if the attribute is defined but is
     *         not an integer.
     *         如果定义的属性不是整形,会抛异常
     *
     * @see #getDimensionPixelOffset
     * @see #getDimensionPixelSize
     */
    public float getDimension(int index, float defValue) {
        if (mRecycled) {
            throw new RuntimeException("Cannot make calls to a recycled instance!");
        }

        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*AssetManager.STYLE_NUM_ENTRIES, value);
            throw new UnsupportedOperationException(
                    "Failed to resolve attribute at index " + index + ": " + value);
        }

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


2.3 getDimensionPixelOffset 的源码

 /**
     * Retrieve a dimensional unit attribute at <var>index</var> for use
     * as an offset in raw pixels.  This is the same as
     * {@link #getDimension}, except the returned value is converted to
     * integer pixels for you.  An offset conversion involves simply
     * truncating the base value to an integer.
     *
     * 通过 参数 index 获取一个在原始像素上使用一个偏移(offset)尺寸单位。除了返回值类型是整形(单位是像素),
     * 其他的和getDimension一样,偏移也涉及到简单的转换(怎么转换呢,其实就是强转为int 类型了)
     * <p>
     * This method will throw an exception if the attribute is defined but is
     * not a dimension.
     * 
     * @param index Index of attribute to retrieve.
     * @param defValue Value to return if the attribute is not defined or
     *                 not a resource.
     *
     * @return Attribute dimension value multiplied by the appropriate
     *         metric and truncated to integer pixels, or defValue if not defined.
     * @throws RuntimeException if the TypedArray has already been recycled.
     * @throws UnsupportedOperationException if the attribute is defined but is
     *         not an integer.
     *
     * @see #getDimension
     * @see #getDimensionPixelSize
     */
    public int getDimensionPixelOffset(int index, int defValue) {
        if (mRecycled) {
            throw new RuntimeException("Cannot make calls to a recycled instance!");
        }

        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*AssetManager.STYLE_NUM_ENTRIES, value);
            throw new UnsupportedOperationException(
                    "Failed to resolve attribute at index " + index + ": " + value);
        }

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

2.4 getDimensionPixelSize 的源码

/**
     * Retrieve a dimensional unit attribute at <var>index</var> for use
     * as a size in raw pixels.  This is the same as
     * {@link #getDimension}, except the returned value is converted to
     * integer pixels for use as a size.  A size conversion involves
     * rounding the base value, and ensuring that a non-zero base value
     * is at least one pixel in size.
     * 通过 参数 index 获取一个在原始像素上的尺寸单位。除了返回值类型是整形(单位是像素),
     * 其他的和getDimension一样,也涉及到简单的转换(怎么转换呢,其实是四舍五入成整形了)
     * <p>
     * This method will throw an exception if the attribute is defined but is
     * not a dimension.
     *
     * @param index Index of attribute to retrieve.
     * @param defValue Value to return if the attribute is not defined or
     *                 not a resource.
     *
     * @return Attribute dimension value multiplied by the appropriate
     *         metric and truncated to integer pixels, or defValue if not defined.
     * @throws RuntimeException if the TypedArray has already been recycled.
     * @throws UnsupportedOperationException if the attribute is defined but is
     *         not a dimension.
     *
     * @see #getDimension
     * @see #getDimensionPixelOffset
     */
    public int getDimensionPixelSize(int index, int defValue) {
        if (mRecycled) {
            throw new RuntimeException("Cannot make calls to a recycled instance!");
        }

        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*AssetManager.STYLE_NUM_ENTRIES, value);
            throw new UnsupportedOperationException(
                    "Failed to resolve attribute at index " + index + ": " + value);
        }

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

三、分析

3.1从上面的三段源码来看,我们应该看到这一段。这三句就是三个方法的区别,追进去看下吧。

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


上面三段代码对应以下的三个方法,可以看出返回值上的区别了。

     

 public static float complexToDimension(int data, DisplayMetrics metrics) {
        return applyDimension(
                (data >> COMPLEX_UNIT_SHIFT) & COMPLEX_UNIT_MASK,
                complexToFloat(data),
                metrics);
    }

    public static int complexToDimensionPixelOffset(int data,
                                                    DisplayMetrics metrics) {
        /**
         * 这里只是强转,相当于调用了 return (int)complexToDimension(data,metrics)
         */
        return (int) applyDimension(
                (data >> COMPLEX_UNIT_SHIFT) & COMPLEX_UNIT_MASK,
                complexToFloat(data),
                metrics);
    }

    public static int complexToDimensionPixelSize(int data,
                                                  DisplayMetrics metrics) {
        final float value = complexToFloat(data);
        /**
         * 这一步相当于  f = complexToDimension(data,metrics),下面就是将f 变化了
         */
        final float f = applyDimension(
                (data >> COMPLEX_UNIT_SHIFT) & COMPLEX_UNIT_MASK,
                value,
                metrics);
        /**
         * 这一步就是四舍五入
         */
        final int res = (int) (f + 0.5f);
        if (res != 0) return res;
        if (value == 0) return 0;
        if (value > 0) return 1;
        return -1;
    }

好了,至此我们就简单的看了三个方法的区别,另外再补充说下,开头说到 的 Resources 类中三个相应的方法,最终也是这里的区别,这里就不多说了。

四,简单的验证

4.1 自定义控件(省略)

4.2 自定义属性 (省略)

4.3 在自定义控件中获取属性值

 private void init(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.WaterFallLayout);
        mColums = array.getInteger(R.styleable.WaterFallLayout_colum, DEFAULT_COLUMS);
        float dimension = array.getDimension(R.styleable.WaterFallLayout_margin,DEFAULT_MARGIN);
        int pixelSize = array.getDimensionPixelSize(R.styleable.WaterFallLayout_margin,DEFAULT_MARGIN);
        int pixelOffset = array.getDimensionPixelOffset(R.styleable.WaterFallLayout_margin,DEFAULT_MARGIN);
        array.recycle();

        Log.e(TAG, "getDimension==>" + dimension);
        Log.e(TAG, "getDimensionPixelSize==>" + pixelSize);
        Log.e(TAG, "getDimensionPixelOffset==>" + pixelOffset);
    }


          4.4 在布局文件中引用该自定义控件(省略,布局中传入margin 参数值是14.5dp),运行得到下面结果

                09-07 03:34:17.223 25181-25181/com.example.test1 E/WaterFallLayout:getDimension==>43.5
09-07 03:34:17.223 25181-25181/com.example.test1 E/WaterFallLayout:getDimensionPixelSize</span>==>44
09-07 03:34:50.231 25181-25181/com.example.test1 E/WaterFallLayout:getDimensionPixelOffset</span>==>43

、结论

5.1 三个方法 区别就是 返回值类型不同,getDimension 返回float    getDimensionPixelOffset 强转为int,而 getDimensionPixelSize 是四舍五入。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值