TypedValue 和 TypedArray

获取自定义属性值常会使用TypedValue 和 TypedArray 类

TypedValue

  • applyDimension ( return float )
  • complexToDimension (return float)

TypedArray

  • getDimension (return float)

下面从一个小例子逐步的解释这几个方法

在自定义View构造体中获取属性经常会这样写:

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MiUiClockView);
         int n = typedArray.getIndexCount();

        int defaultSize = (int) TypedValue.applyDimension(  TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics());//默认Paint textSize

        for (int i = 0; i < n; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.MiUiClockView_highColor:
                    mHighColor = typedArray.getColor(attr, Color.WHITE);
                    break;
                case R.styleable.MiUiClockView_textSize:
                    textSize = typedArray.getDimension(attr,defaultSize);
                    Log.e(TAG,textSize+"");
                    break;
            }
        }
        typedArray.recycle();
        Paint paint = new Paint();
        paint.setTextSize(textSize);

xml 定义如下:

   <com.example.javris.paintdemo.MiUiClockView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:highColor="#ffffff"
        app:textSize="12sp"/>

attrs.xml中属性定义如下:

 <declare-styleable name="MiUiClockView">
        <attr name="highColor" format="color"/>
        <attr name="textSize" format="dimension"/>
        <attr name="intSize" format="integer"/>
    </declare-styleable>

这是自定义View获取属性的老套路了。首先Paint 设置textSize的大小单位是sp,这个大家是知道的,从上面代码得知:获取paint 所需要的textSize有两种方案:

TypedArray — getDimension(int index, float defValue)
TypedValue — applyDimension(int unit, float value,DisplayMetrics metrics)

TypeValue applyDimension()

首先看一下TextView setTextSize()方法源码

    public void setTextSize(float size) {
        setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
    }

  public void setTextSize(int unit, float size) {
        Context c = getContext();
        Resources r;

        if (c == null)
            r = Resources.getSystem();
        else
            r = c.getResources();

        setRawTextSize(TypedValue.applyDimension(
                unit, size, r.getDisplayMetrics()));
    }

  private void setRawTextSize(float size) {
        if (size != mTextPaint.getTextSize()) {
            mTextPaint.setTextSize(size);

            if (mLayout != null) {
                nullLayouts();
                requestLayout();
                invalidate();
            }
        }
    }

TypeValue类

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

其中textView 的单位为sp所以单位参数为COMPLEX_UNIT_SP,通过调用setRawTextSize方法给Paint设置textSize,而paint所需的textSize值单位位px,正是通过TypeValue 的 applyDimension()方法进行转换的,从Switch case 语句中可以很容易看出 sp 转px dp 转 px 。

那么现在做出如下总结:

TypeValue的applyDimension方法其作用就是通过传递不同的单位和数值,最终转换成为像素值.

TypedArray getDimension()
 public float getDimension(@StyleableRes int index, float defValue) {
          ... //省略部分代码

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

代码比较长省去一些,可以看到调用了TypedValue的complexToDimension(),看一下这个方法的源码

TypeValue 类

    public static final int COMPLEX_UNIT_SHIFT = 0;
    public static final int COMPLEX_UNIT_MASK = 0xf;

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

可以看到又调用到了applyDimension()方法,第一个参数为单位, 那么(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK 运算的目的就是为了让data 0-3位和0xf (二级制 1111)进行与运算,所以Data的低四位存储的是单位。

第二个参数则是通过complexToFloat()方法。

 public static final int COMPLEX_MANTISSA_MASK = 0xffffff;
 public static final int COMPLEX_MANTISSA_SHIFT = 8;
 private static final float[] RADIX_MULTS = new float[] {
        1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT,
        1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT
    };
 public static final int COMPLEX_RADIX_SHIFT = 4;
 public static final int COMPLEX_RADIX_MASK = 0x3;

public static float complexToFloat(int data)
    {
        return (data&(TypedValue.COMPLEX_MANTISSA_MASK
                   <<TypedValue.COMPLEX_MANTISSA_SHIFT))
            * RADIX_MULTS[(data>>TypedValue.COMPLEX_RADIX_SHIFT)
                            & TypedValue.COMPLEX_RADIX_MASK];
    }

0xffffff ‭ 转成2进制 1111 1111 1111 1111 1111 1111‬ 左边移动8位 则为1111 1111 1111 1111 1111 1111‬ 0000 0000 这就是一个32位的整形,data& (…)实际上也就想得到高24位的值,在看RADIX_MULTS[….] data>> 4 向右移动4位,还记得上面complexToDimension 方法中data 低四位(0-3位)是为了求换算单位,所以data 要先右移4位然后在和0x3 (二进制 0011)作与运算,这样实际上获取data 4-7位数值,并且最大数值超不过3,这样就能在RADIX_MULTS数组中选择一个数值了,这样来看实际上data 数据的低4位存储着数值的单位而高24则存储着数值。最终通过TypeValue的applyDimension 依旧可以得到px.

那么现在做出如下总结:

TypeArray 的getDimension()方法实际上则是通过调用TypeValue的complexToDimension()方法实现的,complexToDimension中传递的data 数据 0-3存储着转换单位,4-7位为了获取一个radix ,高24位则存储着数值。

其实像这种形式的存储数据方式在Android 中并不少见,比例MeasureSpec 同样是一个int 类型,它的高2位存储的是Mode,低30位存储的是Size.目的为的就是减少内存的分配。对MeasureSpec不理解可参考博客

http://blog.csdn.net/wning1/article/details/64137354

ok,到这TypeValue 和 TypeArray 就算讲完了,你可能会有疑问TypeValue中complexToDimen中传递的data为什么低4位就存储着单位,高24位存储着数值呢?实际上这是系统在解析自定义xml布局的时候如: app:textSize=”12sp”时就会将数值12和单位sp利用位运算存储到data中了,在这就不再看android中的源码。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值