澳门新葡萄京娱乐场 1

澳门新葡萄京娱乐场TypedArray流程分析

在自定义控件的时候,即使大家想额外的增加部分属性,就能用到TypedArray那么些类,那么那个类是怎么取得的,以致怎么利用的,这篇讲会详细解说,上面是自己此前自定义控件的一段代码

澳门新葡萄京娱乐场 1

自定义属性

TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.myaccount_item_style);

obtainStyledAttributes_flow.png

1 自定义属性的宗旨步骤

我们看来TypedArray是通过Context的艺术获得的,但要记住落成今后自然要调用recycle(State of Qatar方法开展回笼,大家点进去找到最终实现

Context#obtainStyledAttributes

// 调用Resources.Theme的obtainStyledAttributes方法
return getTheme().obtainStyledAttributes(attrs);

在Context中的getTheme方法是抽象方法,那大家得看他的子类的现实性完成,大家平常会在自定义View的时候调用此办法,而自定义View中的Context是传进来的,平时指的是它呈现的Activity,大家在Activity中检索getTheme方法,会寻找到它的父类ContextThemeWrapper中,那我们来探望ContextThemeWrapper中getTheme怎么贯彻的:

      a 自定义三个CustomView世袭View

public TypedArray obtainStyledAttributes(AttributeSet set,  
        int[] attrs, int defStyleAttr, int defStyleRes) {  
    final int len = attrs.length;  
    final TypedArray array = TypedArray.obtain(Resources.this, len);  

    // XXX note that for now we only work with compiled XML files.  
    // To support generic XML files we will need to manually parse  
    // out the attributes from the XML file (applying type information  
    // contained in the resources and such).  
    final XmlBlock.Parser parser = (XmlBlock.Parser)set;  
    AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,  
            parser != null ? parser.mParseState : 0, attrs, array.mData, array.mIndices);  

    array.mTheme = this;  
    array.mXml = parser;  

……………  
    return array;  
}

ContextThemeWrapper#getTheme

  1. 依附版本选用默许主旨并保存在mThemeResource中

mThemeResource = Resources.selectDefaultTheme(mThemeResource,
        getApplicationInfo().targetSdkVersion);
  1. 开头化核心

initializeTheme();

在initializeTheme方法内部的落到实处原理:最后调用了AssetManager的native方法applyThemeStyle

      b 在valus下编辑 或 创建 attrs.xml例如:

我们先看上边AssetManager的applyStyle方法是native方法,也正是用C++完结的,他会提取自定义控件属性的的值保存TypedArray中的mData数组中,这么些数组的高低是由你定义控件属性的个数调整的,是她的6倍,下面的attrs其实就是您自定义属性的个数,我们来看一下

Context#obtainStyledAttributes

Context类中有4个obtainStyledAttributes
最终调用的都以4个参数的obtainStyledAttributes办法,而最终调用的是ResourcesImpl.ThemeImpl的obtainStyledAttributes艺术。让大家看看Context的obtainStyledAttributes办法的4个参数分别代表着怎么样:

  • AttributeSet set
    :AttributeSet是在布局中定义的一多级属性的集聚,富含系统定义的性情。在下列例子中,如layout_width,还会有自定义的习性,如MyProgress
  • @StyleableRes int[] attrs
    :自定义属性集结,在下列例子中,如R.styleable.MyView
  • @AttrRes int defStyleAttr
    :在这里时此刻核心中有一個援用指向样式文件,這個样式文件将 TypedArray
    设置私下认可值。借使此参数为0即表示不开展默许值设置。在下列例子中,如R.attr.DefaultViewStyleAttr
  • @StyleRes int defStyleRes :暗中认可的体裁财富文件,独有当 defStyleAttr
    为 0
    或许无法在相应的焦点下找到能源文件时才起效果。借使此参数为0即表示不实行默许值设置。在下列例子中,如R.style.DefaultViewStyleRes

以自定义View为例,以后开立二个MyView:

public MyView(Context context, @Nullable AttributeSet attrs) {
    Logger logger = Logger.getLogger("MyView");
    for (int i = 0; i < attrs.getAttributeCount(); i++) {
        logger.info(attrs.getAttributeName(i) + " : " + attrs.getAttributeValue(i));
    }
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView, R.attr.DefaultViewStyleAttr, R.style.DefaultViewStyleRes);
    logger.info("-----------------------------------------");
    logger.info("MyText1的最终值" + " : " + a.getString(R.styleable.MyView_MyText1));
    logger.info("MyText2的最终值" + " : " + a.getString(R.styleable.MyView_MyText2));
    logger.info("MyText3的最终值" + " : " + a.getString(R.styleable.MyView_MyText3));
    logger.info("MyText4的最终值" + " : " + a.getString(R.styleable.MyView_MyText4));
    logger.info("MyText5的最终值" + " : " + a.getString(R.styleable.MyView_MyText5));
}

在attr.xml中自定义属性:

<declare-styleable name="MyView">
    <attr name="MyText1" format="string" />
    <attr name="MyText2" format="string" />
    <attr name="MyText3" format="string" />
    <attr name="MyText4" format="string" />
    <attr name="MyText5" format="string" />
</declare-styleable>

在styles.xml中自定义style

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="DefaultViewStyleAttr">@style/MyViewStyleAttr</item>
    <item name="MyText1">"defStyleAttr中提供的默认的属性值,在主题中直接定义"</item>
    <item name="MyText2">"defStyleAttr中提供的默认的属性值,在主题中直接定义"</item>
    <item name="MyText3">"defStyleAttr中提供的默认的属性值,在主题中直接定义"</item>
    <item name="MyText4">"defStyleAttr中提供的默认的属性值,在主题中直接定义"</item>
</style>

<style name="MyViewStyle">
    <item name="MyText1">"XML中在style里定义的属性值"</item>
    <item name="MyText2">"XML中在style里定义的属性值"</item>
</style>

<style name="DefaultViewStyleRes">
    <item name="MyText1">"defStyleRes中提供的默认的属性值"</item>
    <item name="MyText2">"defStyleRes中提供的默认的属性值"</item>
    <item name="MyText3">"defStyleRes中提供的默认的属性值"</item>
    <item name="MyText4">"defStyleRes中提供的默认的属性值"</item>
    <item name="MyText5">"defStyleRes中提供的默认的属性值"</item>
</style>

<attr name="DefaultViewStyleAttr" format="reference" />

<style name="MyViewStyleAttr">
    <item name="MyText1">"defStyleAttr中提供的默认的属性值,在主题中的style里定义"</item>
    <item name="MyText2">"defStyleAttr中提供的默认的属性值,在主题中的style里定义"</item>
    <item name="MyText3">"defStyleAttr中提供的默认的属性值,在主题中的style里定义"</item>
</style>

在布局文件中援用这一个MyView:

<com.cn.zero.gesture.MyView 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/MyViewStyle"
    app:MyText1="XML中直接定义的属性值" />

运维之后输出的结果是:

I/MyView: layout_width : -1
I/MyView: layout_height : -2
I/MyView: MyText1 : XML中直接定义的属性值
I/MyView: style : @style/MyViewStyle
I/MyView: -----------------------------------------
I/MyView: MyText1的最终值 : XML中直接定义的属性值
I/MyView: MyText2的最终值 : XML中在style里定义的属性值
I/MyView: MyText3的最终值 : defStyleAttr中提供的默认的属性值,在主题中的style里定义
I/MyView: MyText4的最终值 : defStyleAttr中提供的默认的属性值,在主题中直接定义
I/MyView: MyText5的最终值 : null

从上边的结果来看,xml attributes > xml style > theme style
defStyleAttr > theme defStyleAttr > defStyleRes

**上边例子的代码不改变,大家将defStyleAttr设为0,如: **

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView, 0, R.style.DefaultViewStyleRes);

运营之后输出的结果是:

I/MyView: layout_width : -1
I/MyView: layout_height : -2
I/MyView: MyText1 : XML中直接定义的属性值
I/MyView: style : @style/MyViewStyle
I/MyView: -----------------------------------------
I/MyView: MyText1的最终值 : XML中直接定义的属性值
I/MyView: MyText2的最终值 : XML中在style里定义的属性值
I/MyView: MyText3的最终值 : defStyleRes中提供的默认的属性值
I/MyView: MyText4的最终值 : defStyleRes中提供的默认的属性值
I/MyView: MyText5的最终值 : defStyleRes中提供的默认的属性值

那时,MyText3、MyText4、MyText5的末梢值都产生了在DefaultViewStyleRes中定义的品质的值了。能够摸清在defStyleAttr中检索不到值,才会去取defStyleRes中装置的值。

相同安装属性的私下认可值,都会动用defStyleRes来设置。

   
 当中resource是跟标签,能够在内部定义若干个declare-styleable,中name定义了变量的名目,下边能够再自定义八个个性,针对来讲,其属
     
 性的称号为”text”,format钦点了该属性类型为string,只好表示字体的大大小小。

static TypedArray obtain(Resources res, int len) {  
    final TypedArray attrs = res.mTypedArrayPool.acquire();  
    if (attrs != null) {  
        attrs.mLength = len;  
        attrs.mRecycled = false;  

        final int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;  
        if (attrs.mData.length >= fullLen) {  
            return attrs;  
        }  

        attrs.mData = new int[fullLen];  
        attrs.mIndices = new int[1 + len];  
        return attrs;  
    }  

    return new TypedArray(res,  
            new int[len*AssetManager.STYLE_NUM_ENTRIES],  
            new int[1+len], len);  
}

Context#obtainStyledAttributes

return getTheme().obtainStyledAttributes(
    set, attrs, defStyleAttr, defStyleRes);

     format还足以钦赐别的的门类举例;

他先是会从TypedArray池中拿走,尽管有就抽出,mDate的分寸不能小于属性个数的6倍,因为STYLE_NUM_ENTWranglerIES的值为6,如果未有就new一个然后再次回到,把品质的值提收取来现在大家就能够来操作了,大家先来看一下View类起头化中的一段代码

ResourcesImpl.ThemeImpl#obtainStyledAttributes

  1. 调用TypedArray的obtain方法

final TypedArray array = TypedArray.obtain(Resources.this, len);
  1. 调用当地点法给array(TypedArray卡塔尔国的mData、mIndices赋值

AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices);

查阅本地点法applyStyle的实际达成,是在mData数组中储存了六类别型的多寡,分别为:

  • STYLE_TYPE
  • STYLE_DATA
  • STYLE_ASSET_COOKIE
  • STYLE_RESOURCE_ID
  • STYLE_CHANGING_CONFIGURATIONS
  • STYLE_DENSITY
  • STYLE_NUM_ENTRIES
    base/core/jni/android_util_AssetManager.cpp查看android_content_AssetManager_applyStyle

// Write the final value back to Java.
dest[STYLE_TYPE] = value.dataType;
dest[STYLE_DATA] = value.data;
dest[STYLE_ASSET_COOKIE] = block != kXmlBlock ?
     static_cast<jint>(res.getTableCookie(block)) : -1;
dest[STYLE_RESOURCE_ID] = resid;
dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
dest[STYLE_DENSITY] = config.density;

     reference   表示援用,参照他事他说加以考查某一财富ID

final int N = a.getIndexCount();  
for (int i = 0; i < N; i++) {  
    int attr = a.getIndex(i);  
    switch (attr) {  
        case com.android.internal.R.styleable.View_background:  
            background = a.getDrawable(attr);  
            break;  
…………………  
    }  
}

TypedArray#obtain

  1. 从Resource.mTypedArrayPool(SynchronizedPool<TypedArray>)池中取TypedArray对象

final TypedArray attrs = res.mTypedArrayPool.acquire();
...
  1. 没取到,则调用TypedArray的布局方法

return new TypedArray(res,
                new int[len*AssetManager.STYLE_NUM_ENTRIES],
                new int[1+len], len);

在TypedArray中我们会见到点不清getxxx()形式,我们点步向看会开掘多数都会有这么一行代码:

if (mRecycled) {
    throw new RuntimeException("Cannot make calls to a recycled instance!");
}

推断:这段代码会不会和每趟在自定义View中取完自定义属性之后调用的typedArray.recycle();有关?

if (mRecycled) {
    throw new RuntimeException(toString() + " recycled twice!");
}
mRecycled = true;
// These may have been set by the client.
...
mResources.mTypedArrayPool.release(this);

查看recycle()措施,可以精晓Android必要我们在每回不再动用TypedArray时,必需手动调用该情势以复用TypedArray
注意:

  1. 不能够重新调用该措施,不然会抛出以下极度:

Caused by: java.lang.RuntimeException: [0, 0, 1, 0, ...] recycled twice!
  1. 不能够在调用该措施后,还调用getxxx等TypedArray的法子,不然回抛出以下分外:

Caused by: java.lang.RuntimeException: Cannot make calls to a recycled instance!

    string   表示字符串

他会把TypedArray中的数据提抽出来对View的品质赋值,大家来看一下TypedArray类的布局方法

TypedArray#getInt

  1. 基于下标index获取mData数组存款和储蓄的Type类型的值,决断Type是或不是为TypedValue.TYPE_NULL,true则,重返默许值defValue

...
final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
            return defValue;
        } 
  1. 依靠下标index获取data、assetCookie、resourceId、changingConfigurations、density等品类的值,并储存在TypedValue中

getValueAt(index, v)
  1. 经过XmlUtils.convertValueToInt方法将诸如”-12,0xa1,014,#fff”那类字符串转变为实在的数值

return XmlUtils.convertValueToInt(v.coerceToString(), defValue);

    color   表示颜色值

/*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) {  
    mResources = resources;  
    mMetrics = mResources.mMetrics;  
    mAssets = mResources.mAssets;  
    mData = data;  
    mIndices = indices;  
    mLength = len;  
}

TypedArray$getValueAt

将mData数组数组中的数据存储在TypedValue中:

...
outValue.type = type;
outValue.data = data[index+AssetManager.STYLE_DATA];
outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
outValue.density = data[index+AssetManager.STYLE_DENSITY];
outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;

   dimension   表示尺寸值

代码相当的轻松,此中mData正是正是从xml文件中提取到的数额,mData的大大小小是自定义属性个数的6倍,所以这里是每6个作为一组,大家得以看一下上边的obtain方法中data数组的轻重是乘以6(STYLE_NUM_ENT福特ExplorerIES)的,那6连串型如下,定义在AssetManager类中,上面包车型大巴第贰个代表每组6个

XmlUtils$convertValueToInt

  1. 改造负数

if ('-' == nm.charAt(0)) {
    sign = -1;
    index++;
}
  1. 调换十八进制和八进制

if ('0' == nm.charAt(index)) {
    //  Quick check for a zero by itself
    if (index == (len - 1))
        return 0;
    char    c = nm.charAt(index + 1);
    if ('x' == c || 'X' == c) {
        index += 2;
        base = 16;
    } else {
        index++;
        base = 8;
    }
}
  1. 更改颜色数值

else if ('#' == nm.charAt(index))
{
    index++;
    base = 16;
}
  1. 将String转换来数值

Integer.parseInt(nm.substring(index), base) * sign;

   boolean   表示布尔值

/*package*/ static final int STYLE_NUM_ENTRIES = 6;  
/*package*/ static final int STYLE_TYPE = 0;  
/*package*/ static final int STYLE_DATA = 1;  
/*package*/ static final int STYLE_ASSET_COOKIE = 2;  
/*package*/ static final int STYLE_RESOURCE_ID = 3;  
/*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4;  
/*package*/ static final int STYLE_DENSITY = 5;

总结

  1. TypedArray是用来搜寻项目中种种财富的
  2. 唯有当 defStyleAttr 为 0
    只怕不可能在相应的主旨下找到财富文件时才起效能,defStyleRes中定义的私下认可样式才起效果
  3. TypedArray检索完能源,必得调用recycle方法来循环利用

参考:
A deep dive into Android View
constructors
http://blog.csdn.net/luoshengyang/article/details/8738877

   integer   表示整型值

对应着TypedValue类中的那7中项目,此中string是依赖type获得的

   float   表示浮点值

/** The type held by this value, as defined by the constants here. 
 *  This tells you how to interpret the other fields in the object. */  
public int type;  

/** If the value holds a string, this is it. */  
public CharSequence string;  

/** Basic data in the value, interpreted according to {@link #type} */  
public int data;  

/** Additional information about where the value came from; only 
 *  set for strings. */  
public int assetCookie;  

/** If Value came from a resource, this holds the corresponding resource id. */  
public int resourceId;  

/** If Value came from a resource, these are the configurations for which 
 *  its contents can change. */  
public int changingConfigurations = -1;  

/** 
 * If the Value came from a resource, this holds the corresponding pixel density. 
 * */  
public int density;

   fraction   表示百分数

借使我们认真看的时候就能意识obtain方法中对mIndices数组初叶化的时候是加1的,因为mIndices数组的首先个保存的是我们所利用质量的个数,记住是利用不是概念,大家来看一下里面的一些代码

   enum   表示枚举值

/** 
 * Return the number of values in this array. 
 */  
public int length() {  
    if (mRecycled) {  
        throw new RuntimeException("Cannot make calls to a recycled instance!");  
    }  

    return mLength;  
}  

/** 
 * Return the number of indices in the array that actually have data. 
 */  
public int getIndexCount() {  
    if (mRecycled) {  
        throw new RuntimeException("Cannot make calls to a recycled instance!");  
    }  

    return mIndices[0];  
}

   flag   表示位运算

首先个length重回的是我们所定义属性的个数,因为那几个参数是在布局函数中赋值的,传递的是int[]
attrs的长短,而以此sttrs正是我们在attrs文件中自定义属性的时候在Tiguan文件中自动生成的二个数组。而上边包车型大巴getIndexCount(卡塔尔方法重回的是我们所选择的质量个数,因为mIndices的多少是从xml文件中提取的,第一个岗位保存的是大家采取质量的个数,前面的职分正是大家运用的自定义属性在福特Explorer文件中变化的id,在看二个方式,也是自定义的时候常用到的

c在构造文件中CustomView使用自定义的习性(注意namespace)

public int getIndex(int at) {  
    if (mRecycled) {  
        throw new RuntimeException("Cannot make calls to a recycled instance!");  
    }  

    return mIndices[1+at];  
}

d 在CustomView的布局方法中经过TypedArray获取

以此获得的正是自定义属性在Enclave文件中变化的id,剩下的部分主意正是从TypedArray中提取值了,首要有以下几连串型

     TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.test);

<declare-styleable name="CustomTheme">  
    <attr name="textView1" format="string" />  
    <attr name="textView2" format="boolean" />  
    <attr name="textView3" format="integer" />  
    <attr name="textView4" format="float" />  
    <attr name="textView5" format="color" />  
    <attr name="textView6" format="dimension" />  
    <attr name="textView7" format="fraction" />  
    <attr name="textView8" format="reference" />  
    <attr name="textView9" format="enum" />  
    <attr name="textView10" format="flags" />  
</declare-styleable>

     test_mytextview =
typedArray.getString(R.styleable.test_mytextview);

TypedArray方法超级多,这里就捡常用的几个来解析一下,在解析早先先看一下上面那些方法

    int test_mytest = typedArray.getInteger(R.styleable.test_mytest,
-1);

private CharSequence loadStringValueAt(int index) {  
    final int[] data = mData;  
    final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];  
    if (cookie < 0) {  
        if (mXml != null) {  
            return mXml.getPooledString(  
                data[index+AssetManager.STYLE_DATA]);  
        }  
        return null;  
    }  
    return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]);  
}

     Log.e(“test”, “test_mytextview = ” + test_mytextview + ” ,
test_mytest = ” + test_mytest);

地点所说的每6个一组,此中每组下标为STYLE_ASSET_总经理KIE(2)的是用来标识缓存的,何况是只对String类型的,我们来看一下

    typedArray.recycle();

private boolean getValueAt(int index, TypedValue outValue) {  
    final int[] data = mData;  
    final int type = data[index+AssetManager.STYLE_TYPE];  
    if (type == TypedValue.TYPE_NULL) {  
        return false;  
    }  
    outValue.type = type;  
    outValue.data = data[index+AssetManager.STYLE_DATA];  
    outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];  
    outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];  
    outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];  
    outValue.density = data[index+AssetManager.STYLE_DENSITY];  
    outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;  
    return true;  
}

2 深切摸底自定义属性(AttributeSet)

下面那些方法是把mData钦命范围的6个数据提取到outValue中,此中string值通过type类型取得的,大家再来看一下assetCookie的笺注

public MyTextView(Context context, AttributeSet attrs) {

/** Additional information about where the value came from; only 
 *  set for strings. */  
public int assetCookie;

super(context, attrs);

故此她只针对String类型,大家再来看一下String类型的注释

}

/** The <var>string</var> field holds string data.  In addition, if 
 *  <var>data</var> is non-zero then it is the string block 
 *  index of the string and <var>assetCookie</var> is the set of 
 *  assets the string came from. */  
public static final int TYPE_STRING = 0x03;

布局方法中AttributeSet 也能博得自定义属性的 值  上边方法能够赢得出
xml中颇负的key value

故此她只针对string类型的数码实行领取,比如text,String,color等,color能够是string类型也得以是int类型,还看上面的loadStringValueAt方法,如若cookie小于0,表明未有缓存,就能够从xml中深入分析,不然就从缓存中取

int attributeCount = attrs.getAttributeCount();

/*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) {  
    // Cookies map to string blocks starting at 1.  
    return mStringBlocks[cookie - 1].get(id);

for(int i = 0;i

咱俩来看一下是怎么从xml中深入分析的,看见下面的obtainStyledAttributes方法,会开采这么一段代码 array.mXml

parser;当中parser就是View及其子类在起先化的时候传递的AttributeSet,我们在头里的《Android
LayoutInflater源码深入分析及使用(二)》中讲到,View及其子类创设的时候是经过反射来初叶化的,大家来回想一下

public final View createView(String name, String prefix, AttributeSet attrs)  
         throws ClassNotFoundException, InflateException {  
     Constructor<? extends View> constructor = sConstructorMap.get(name);  
     Class<? extends View> clazz = null;  

     try {  
    …………………………  
             constructor = clazz.getConstructor(mConstructorSignature);  
    …………………………  

         Object[] args = mConstructorArgs;  
         args[1] = attrs;  

         constructor.setAccessible(true);  
         final View view = constructor.newInstance(args);  
…………………………  
         return view;  

     } catch (NoSuchMethodException e) {  
        …………………………  
     } finally {  
         Trace.traceEnd(Trace.TRACE_TAG_VIEW);  
     }  
 }

中间attrs是透过Resource的loadXmlResourceParser方法加载的,大家看一下

/*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)  
        throws NotFoundException {  
    synchronized (mAccessLock) {  
        TypedValue value = mTmpValue;  
        if (value == null) {  
            mTmpValue = value = new TypedValue();  
        }  
        getValue(id, value, true);  
        if (value.type == TypedValue.TYPE_STRING) {  
            return loadXmlResourceParser(value.string.toString(), id,  
                    value.assetCookie, type);  
        }  
        throw new NotFoundException(  
                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"  
                + Integer.toHexString(value.type) + " is not valid");  
    }  
}

剩余的便是涉及到Xml的解析,这里就不在作浓重商讨,言归正传,还回去刚才的loadStringValueAt方法,假使缓存中设有就从缓存中去,假设不设有就因此xml剖判获取。上面在看一下部分常用的情势,当中getText(int
indexState of Qatar和getString(int index卡塔尔(قطر‎大约,我们就来看一下getString(int
indexState of Qatar方法

public String getString(int index) {  
    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 null;  
    } else if (type == TypedValue.TYPE_STRING) {  
        return loadStringValueAt(index).toString();  
    }  

    TypedValue v = mValue;  
    if (getValueAt(index, v)) {  
        Log.w(Resources.TAG, "Converting to string: " + v);  
        CharSequence cs = v.coerceToString();  
        return cs != null ? cs.toString() : null;  
    }  
    Log.w(Resources.TAG, "getString of bad type: 0x"  
          + Integer.toHexString(type));  
    return null;  
}

上面的index要乘以6(STYLE_NUM_ENTLANDIES),因为是每6个一组的,借使type为null就回去空,要是为String类型就能调用loadStringValueAt方法取得大家设置的值。有几许要静心,要是大家在attrs中设置的format类型和大家自定义设置的参数不符的话,当运营的时候是会报错的,必定要安装切归并clean技能缓和。否则就履行下边包车型大巴措施,强迫转变为字符串,代码比较简单,这里就不再贴出。在来看下一个bool类型和int类型的,由于那八个差不离,就随意挑多个

public int getInt(int index, int defValue) {  
    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_FIRST_INT  
        && type <= TypedValue.TYPE_LAST_INT) {  
        return data[index+AssetManager.STYLE_DATA];  
    }  

    TypedValue v = mValue;  
    if (getValueAt(index, v)) {  
        Log.w(Resources.TAG, "Converting to int: " + v);  
        return XmlUtils.convertValueToInt(  
            v.coerceToString(), defValue);  
    }  
    Log.w(Resources.TAG, "getInt of bad type: 0x"  
          + Integer.toHexString(type));  
    return defValue;  
}

上边的品类倘若当先TYPE_FIRST_INT何况小于TYPE_LAST_INT的时候就从mDate中领取值,这么些不精晓为啥要这么写,然则从她的限量来看也就int,Boolean,color三种是如此取值的

/** Identifies the start of integer values that were specified as 
 *  color constants (starting with '#'). */  
public static final int TYPE_FIRST_COLOR_INT = 0x1c;  

/** The <var>data</var> field holds a color that was originally 
 *  specified as #aarrggbb. */  
public static final int TYPE_INT_COLOR_ARGB8 = 0x1c;  
/** The <var>data</var> field holds a color that was originally 
 *  specified as #rrggbb. */  
public static final int TYPE_INT_COLOR_RGB8 = 0x1d;  
/** The <var>data</var> field holds a color that was originally 
 *  specified as #argb. */  
public static final int TYPE_INT_COLOR_ARGB4 = 0x1e;  
/** The <var>data</var> field holds a color that was originally 
 *  specified as #rgb. */  
public static final int TYPE_INT_COLOR_RGB4 = 0x1f;  

/** Identifies the end of integer values that were specified as color 
 *  constants. */  
public static final int TYPE_LAST_COLOR_INT = 0x1f;  

/** Identifies the end of plain integer values. */  
public static final int TYPE_LAST_INT = 0x1f;

就算界定不在TYPE_FIRST_INT和TYPE_LAST_INT之间,就能够把mData钦命地方上的值提取到TypedValue中,然后在强逼转化,若无领到到就能够重回一个暗中同意值,因为借使在attrs中定义但从不应用,就能回去一个暗中认可值。我们来看一下是怎么转车的

public static final int  
convertValueToInt(CharSequence charSeq, int defaultValue)  
{  
    if (null == charSeq)  
        return defaultValue;  

    String nm = charSeq.toString();  

    // XXX This code is copied from Integer.decode() so we don't  
    // have to instantiate an Integer!  

    int value;  
    int sign = 1;  
    int index = 0;  
    int len = nm.length();  
    int base = 10;  

    if ('-' == nm.charAt(0)) {  
        sign = -1;  
        index++;  
    }  

    if ('0' == nm.charAt(index)) {  
        //  Quick check for a zero by itself  
        if (index == (len - 1))  
            return 0;  

        char    c = nm.charAt(index + 1);  

        if ('x' == c || 'X' == c) {  
            index += 2;  
            base = 16;  
        } else {  
            index++;  
            base = 8;  
        }  
    }  
    else if ('#' == nm.charAt(index))  
    {  
        index++;  
        base = 16;  
    }  

    return Integer.parseInt(nm.substring(index), base) * sign;  
}

本条很好精晓,转化为int类型有0开端的8进制,0x在此以前的16进制,还应该有#开端的color值,假使转会此前是负数,转变之后还要乘以-1(sign)。再来看一个

public int getColor(int index, int defValue) {  
    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_FIRST_INT  
        && type <= TypedValue.TYPE_LAST_INT) {  
        return data[index+AssetManager.STYLE_DATA];  
    } else if (type == TypedValue.TYPE_STRING) {  
        final TypedValue value = mValue;  
        if (getValueAt(index, value)) {  
            ColorStateList csl = mResources.loadColorStateList(  
                    value, value.resourceId);  
            return csl.getDefaultColor();  
        }  
        return defValue;  
    }  

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

本条就无须多说了,因为color有String和int两类别型,假设是String类型就能回来ColorStateList的默许值,因为ColorStateList大概有几许连串型,但一定要都以false的才是暗中同意的,上面随意看叁个,下面那一个提取之后暗许的正是green,因为唯有他的富有意况都以false。

<?xml version="1.0" encoding="utf-8"?>  
<selector xmlns:android="http://schemas.android.com/apk/res/android">  

    <item android:state_pressed="true" android:color="@color/blue"/>  
    <item android:state_pressed="false" android:state_selected="true" android:color="@color/yellow"/>  
    <item android:state_pressed="false" android:state_selected="false" android:color="@color/green"/>  

</selector>

上面在看最终三个方式

public int getLayoutDimension(int index, String name) {  
    index *= AssetManager.STYLE_NUM_ENTRIES;  
    final int[] data = mData;  
    final int type = data[index+AssetManager.STYLE_TYPE];  
    if (type >= TypedValue.TYPE_FIRST_INT  
            && type <= TypedValue.TYPE_LAST_INT) {  
        return data[index+AssetManager.STYLE_DATA];  
    } else if (type == TypedValue.TYPE_DIMENSION) {  
        return TypedValue.complexToDimensionPixelSize(  
            data[index+AssetManager.STYLE_DATA], mResources.mMetrics);  
    }  

    throw new RuntimeException(getPositionDescription()  
            + ": You must supply a " + name + " attribute.");  
}

看方法名大致就精晓是取得layout的尺码的,大概看一下,在ViewGroup中

protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {  
    width = a.getLayoutDimension(widthAttr, "layout_width");  
    height = a.getLayoutDimension(heightAttr, "layout_height");  
}

中间赢得到的值有3种,一种是标准的我们给的大于0的,一种是-1(MATCH_PARENT),另一种是-2(WRAP_CONTENT),记得在讲到《Android
LayoutInflater源码解析及利用(二)》的时候提及,xml的性子除了宽和高以外在最早化的时候基本上都能领取到,但宽和高是可怜的,因为他是终极总结出来的,假使我们自定义View世襲View的时候,要必需重写onMeasure方法,重新总结他的宽和高,若是大家不合算,当我们选用MATCH_PARENT或WRAP_CONTENT属性的时候,结果是完全相近的,尺寸都以填满剩下的显示屏,要是不重写onMeasure方法,在xml文件中把她的宽和高都写死也行,但像这种类型相当不足利索,我们来看一下为什么要重写

public static int getDefaultSize(int size, int measureSpec) {  
    int result = size;  
    int specMode = MeasureSpec.getMode(measureSpec);  
    int specSize = MeasureSpec.getSize(measureSpec);  

    switch (specMode) {  
    case MeasureSpec.UNSPECIFIED:  
        result = size;  
        break;  
    case MeasureSpec.AT_MOST:  
    case MeasureSpec.EXACTLY:  
        result = specSize;  
        break;  
    }  
    return result;  
}

我们看出View中的getDefaultSize方法,AT_MOST和EXACTLY重返的结果都是一样的,要是想看建议看一下ViewGroup的getChildMeasureSpec方法,那个就不在贴出,能够自身去看。OK,TypedArray中剩下的不二等秘书诀好多也都拾贰分相似,这里就不在一一陈述。

String attributeName = attrs.getAttributeName(i);

String attributeValue = attrs.getAttributeValue(i);

Log.e(“test”, “attributeName = ” + attributeName + ” , attributeValue =
” + attributeValue);

}

发表评论

电子邮件地址不会被公开。 必填项已用*标注