简单图解:
图1、一个参数的 obtainStyledAttributes 函数。
图2、两个参数的 obtainStyledAttributes 函数
图3、四个参数的 obtainStyledAttributes 函数

测试代码:
<!--提取Attrs方式Demo-->
<!--项目的Theme-->
<style name="TestBaseTheme" parent="AppBaseTheme">
<item name="TestAttr">1</item>
<item name="TestAttr2">1</item>
<item name="itemTestAttr">@style/ThemeStyleTestAttr</item>
</style>
<attr name="itemTestAttr" format="reference" />
<style name="ThemeStyleTestAttr">
<item name="TestAttr2">3</item>
</style>
<style name="DefaultAttrStyle">
<item name="TestAttr">2</item>
<item name="TestAttr2">2</item>
</style>
<!--自定义的attr集-->
<declare-styleable name="TestAttrStyleable">
<attr name="TestAttr" format="integer" />
<attr name="TestAttr2" format="integer" />
</declare-styleable>
/*
关键词概述:
attrId:自定义控件时经常用到,位于attrs.xml文件,写在一个与自定义控件同名的 styleable 中作为属性定义。
其实可以脱离 styleable 单独定义
attrIdArray:由多个 attrId 组成
一般会在xml文件里把多个 attrId 组成一个 styleable 来使用,这个 styleable 正是 attrIdArray
当然也可以在代码中 arrayOf(attrId) 来创建 attrIdArray
obtainStyledAttributes():通过theme、style资源,寻找 attrIdArray 中所有 attrId 对应的值(不是声明定义的名字或格式,而是给定的值)。
重载方法有很多,但其实都是寻找 attrId 值的。
typedArray:存放多个 attrId 的值。
*/
//从theme中寻找attrIdArray的值 (这里说的 attrIdArray的值,意思是 attrIdArray 中每个 attrId 的值)
val typedArray1 = theme.obtainStyledAttributes(R.styleable.TestAttrStyleable)
//先从styleId中寻找attrIdArray的值,找不到后从theme中寻找attrIdArray的值
val typedArray2 = theme.obtainStyledAttributes(R.style.DefaultAttrStyle, R.styleable.TestAttrStyleable)
val set: AttributeSet? = null
/* 这个方法比较复杂,有4个参数,但其实也只是按优先级从不同位置寻找attrIdArray的值
1、先从set中寻找attrIdArray的值(set就是LayoutInflater解析布局创建View时传入的,View在布局中写的"layout_width"、"style" 等属性。)
2、再寻找theme中defStyleAttr指定的style,从此style中寻找attrIdArray的值(第三个参数)
这一步不好理解,举个例子,TabLayout源码中此值写的是 attr.tabStyle ,这样写的话,运行时就会从 theme 中寻找有没有设置 tabStyle 这条属性,
如果设置了,就会拿取 tabStyle 对应的值 style,然后再寻找这个 style 中 attrIdArray 的值
3、接着判断 if( 由 defStyleAttr 拿到了 attrIdArray 的值,即使只拿到了一些 attrId 的值 ){
跳过 defStyleRes,直接从 theme 中寻找 attrIdArray 的值
} else {
从 defStyleRes 中寻找 attrIdArray 的值(第四个参数)
从 theme 中寻找 attrIdArray 的值
}
参考:https://www.jianshu.com/p/61b79e7f88fc
*/
val typedArray3 = theme.obtainStyledAttributes(set, R.styleable.TestAttrStyleable, R.attr.itemTestAttr, R.style.DefaultAttrStyle)
val i = typedArray3.getInteger(R.styleable.TestAttrStyleable_TestAttr, -99)
val i2 = typedArray3.getInteger(R.styleable.TestAttrStyleable_TestAttr2, -99)
ToastUtils.toast("" + i + "_" + i2)