如何通过TypeArray读取属性

18 阅读2分钟

先来看一段在XML中配置的代码

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:maxWidth="100dp"
    android:maxHeight="100dp"
    android:orientation="vertical"
    android:src="@drawable/ic_launcher_background"
    android:tintMode="screen" />

如何通过解析配置中的属性,来完成对象的数据初始化。

比如从android:maxHeightsetMaxHeight

先来看一下ImageView构造函数。

public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
        int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    
    final TypedArray a = context.obtainStyledAttributes(
            attrs, R.styleable.ImageView, defStyleAttr, defStyleRes);
    saveAttributeDataForStyleable(context, R.styleable.ImageView,
            attrs, a, defStyleAttr, defStyleRes);
    ...     
    setMaxWidth(a.getDimensionPixelSize(R.styleable.ImageView_maxWidth, Integer.MAX_VALUE));
    setMaxHeight(a.getDimensionPixelSize(R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
    ...
    a.recycle();
}

setMaxHeight(a.getDimensionPixelSize(R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));

通过TypeArray通过R.styleable.ImageView_maxHeight这个Id来读取数据。

public int getDimensionPixelSize(@StyleableRes int index, int defValue) {
    final int attrIndex = index;
    index *= STYLE_NUM_ENTRIES;

    final int[] data = mData;
    final int type = data[index + STYLE_TYPE];
    if (type == TypedValue.TYPE_NULL) {
        return defValue;
    } else if (type == TypedValue.TYPE_DIMENSION) {
        return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
    } else if (type == TypedValue.TYPE_ATTRIBUTE) {
        final TypedValue value = mValue;
        getValueAt(index, value);
    }
}
  • 巧妙的数据结构

mData是一个数组,存储了解析后的属性数据信息,它主要由多组数据组成,每组数据都是一样的结构。

[TYPE, DATA, ASSET_COOKIE, RESOURCE_ID, CHANGING_CONFIGURATIONS, DENSITY,SOURCE_RESOURCE_ID,TYPE, DATA, ASSET_COOKIE, RESOURCE_ID, CHANGING_CONFIGURATIONS, DENSITY,SOURCE_RESOURCE_ID,TYPE, DATA, ASSET_COOKIE, RESOURCE_ID, CHANGING_CONFIGURATIONS, DENSITY,SOURCE_RESOURCE_ID]

以上就是Data的数据结构,你可以理解成间隔7个数就是一个组。假如要获取第三组数据,那就是

index=3,要是获取第三组数据中的

TYPE,那就是index*7+0

DATA,那就是index*7+1

DENSITY,那就是index*7+5

所以只需要知道Index,就可以知道每一个属性的7中数据信息了。关于这7种数据信息的偏移量,源代码中把他们定义成了7个常量。

static final int STYLE_NUM_ENTRIES = 7;
static final int STYLE_TYPE = 0;
static final int STYLE_DATA = 1;
static final int STYLE_ASSET_COOKIE = 2;
static final int STYLE_RESOURCE_ID = 3;
static final int STYLE_CHANGING_CONFIGURATIONS = 4;
static final int STYLE_DENSITY = 5;
static final int STYLE_SOURCE_RESOURCE_ID = 6;

理解完数据结构之后,再回头来看一下,获取dimen的代码,就比较容易理解了。

final int attrIndex = index;
index *= STYLE_NUM_ENTRIES;//算该组开始索引
final int[] data = mData;
final int type = data[index + STYLE_TYPE];//根据偏移量获取数据