TypedValue 和 TypedArray

来源:互联网 发布:淘宝二姐家是正品吗 编辑:程序博客网 时间:2024/06/06 20:25

获取自定义属性值常会使用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 0
原创粉丝点击