Android TypedArray简单分析(一)使用

介绍

  Android TypedArray
  这个类的注释是说它是一组数据的容器,这些数据都是从Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}获取到相应属性对应的值。
  TypedArray类有成员变量mData和mIndices,这两个的类型都是整数数组(int[])。
  mData里面的数据是7个整数为一组来描述一个资源数据的,这七个数据为[type, data, asset cookie, resource id, changing configuration, density,source resource id],其中type是数据类型,对应着TypedValue类的定义中的TYPE_*,data是数据,asset cookie是该值来自于哪一个APK文件资源中,resource id是当前的值是哪一个资源ID解析出来的,changing configuration是该资源的会随哪些配置信息变化重新加载,density是该资源的屏幕密度信息,source resource id是该值是从哪个资源文件或者哪个style解析出来的。
  mIndices中的值第一个为属性数量,代表描述了多少个属性,接着的值就是属性对应的队列序列值,第一个属性对应的序列为0,一直到最后数量-1。在这里,属性被被编译成一个整数值来用来表示。
  该类相关常用的方法
该方法在Context Java类中

    /**
     * Retrieve styled attribute information in this Context's theme.  See
     * {@link android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
     * for more information.
     *
     * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
     */
    @NonNull
    public final TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
            @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
            @StyleRes int defStyleRes) {
        return getTheme().obtainStyledAttributes(
            set, attrs, defStyleAttr, defStyleRes);
    }         

  该方法经常会在控件的初始化代码中调用。其中参数 set:属性值的集合,其实真正的类型是XmlBlock.Parser,它的实现是在c++层,对应ResXMLParser。XML资源文件被打包之后,变成了二进制的资源文件,ResXMLParser就是解析该文件的工具类。XML文件被解析的时候,是一层一层向内解析的,解析到该控件对应的层的时候,就会调用到该控件的构造函数,而这个 obtainStyledAttributes() 方法多数会写在该控件的构造函数中,所以调用到obtainStyledAttributes方法的时候,set里面就包含该控件所有的属性值。
  参数 attrs:是需要解析的属性ID整数集合,这个数组的长度对应着TypedArray实例中的mData和mIndices的长度。如果attrs数组的长度为L,那么mData数组的长度为7*L,mIndices的长度为L+1。mData数组就是按照上面说的7个数据按照顺序排列为一组,总共有L组。mIndices中的值第一个为L,代表描述了多少个属性,接着就是属性对应的序列值,第一个属性对应的值为0,一直到L-1。
  参数 defStyleAttr:属性资源值ID,设置在themes.xml中,设置的对应的值是style或者属性(如果设置的是属性值,会找到Theme下该属性值对应的style,从该style中继续得到属性集合的值)
  参数 defStyleRes:需要查找的style的ID
  后面两个参数,是指属性的值可以在这两个style中去查找。属性的值不光在这两个style中查找,还可以在控件的直接设置属性中查找,在控件的设置的style属性中查找。并且属性的取值在这四个中是有优先级的:1、XML控件里直接设置属性,2、XML控件设置的style属性,3、参数 defStyleAttr,4、参数 defStyleRes

举个例子

  在项目的values文件夹中attrs.xml文件中添加几个资源属性


<resources>
    <declare-styleable name="MyCustomStyleable">
            <attr name="attr1" format="string" />
            <attr name="attr2" format="string" />
            <attr name="attr3" format="string" />
    declare-styleable>
    <attr name="customViewAttr" format="reference" />
resources>

  values文件夹中themes.xml文件中设置如下,注意,在应用的Theme中设置了一个customViewAttr属性,并且将它设置为MyStyleTheme的style。该style中,只设置了attr1属性。

<resources xmlns:tools="http://schemas.android.com/tools">
    
       <style name="Theme.MyTestKotlinApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
          
          "colorPrimary">@color/purple_500
          "colorPrimaryVariant">@color/purple_700
          "colorOnPrimary">@color/white
          
          "colorSecondary">@color/teal_200
          "colorSecondaryVariant">@color/teal_700
          "colorOnSecondary">@color/black
          
          "android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant
          
          "customViewAttr">@style/MyStyleTheme
       style>
       <style name="MyStyle">
         	"attr1">Hello Sty attr1
         	"attr2">Hello Sty attr2
       style>
		<style name="MyStyleTheme">
			"attr1">Hello ThemeSty attr1
		style>
		<style name="MyStyleDef">
			"attr1">Hello DefSty attr1
		style>
resources>

  自定义控件CustomView.kt

class CustomView:View {
    val tag = "CustomView"
    constructor(con:Context):super(con)
    constructor(con:Context, attrs: AttributeSet?):this(con,attrs,R.attr.customViewAttr)
    constructor(con:Context, attrs: AttributeSet?, defStyAttr:Int):super(con,attrs,defStyAttr) {
      var t = con.obtainStyledAttributes(attrs,R.styleable.MyCustomStyleable,0,R.style.MyStyleDef)
      val att1 = t.getString(R.styleable.MyCustomStyleable_attr1)
      val att2 = t.getString(R.styleable.MyCustomStyleable_attr2)
      Log.e(tag,"att1:$att1,attr2:$att2")    }    
      @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
      constructor(con:Context, attrs:AttributeSet?, defStyAttr:Int, defStyRes:Int):super(con,attrs,defStyAttr,defStyRes)
}

  布局文件activity_main.xml文件\n\n



   	<TextView
   	   android:layout_width="wrap_content"
   	   android:layout_height="wrap_content"
   	   android:text="Hello World!"        
   	   android:textSize="@dimen/textSize" 
   	   />    
   	<com.example.mytestxmlres.CustomView
   	   android:layout_width="match_parent"
   	   android:layout_height="wrap_content"
   	   />
LinearLayout>
   	                                   

主Activity文件

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)        
            setContentView(R.layout.activity_main)    
            	}
            }

  按照上面说的四种取值,看看在上面对应的代码的基础上怎么改写
  1、XML控件里直接设置属性
这个改动下布局文件中控件的属性值,如下

    <com.example.mytestxmlres.CustomView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       app:attr1 = "A"
       app:attr2 = "B"
       app:attr3 = "C"
       />
                                            

  2、XML控件设置的style属性
这个改动下布局文件中控件的属性值,如下

    <com.example.mytestxmlres.CustomView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       style="?attr/customViewAttr"
       />

或者改成

    <com.example.mytestxmlres.CustomView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       style="@style//MyStyleTheme"
       />

  这两种写法,第一种是又设置成了一个属性,第二种是直接设置成了一个style。
  3、参数 defStyleAttr
这个参数起作用是在没设置前面两个的情况下,设置在方法obtainStyledAttributes中的第三个参数中,即在上面自定义控件的代码中修改,例如下

var t = con.obtainStyledAttributes(attrs, R.styleable.MyCustomStyleable,R.attr.customViewAttr,R.style.MyStyleDef)
val att1 = t.getString(R.styleable.MyCustomStyleable_attr1)
val att2 = t.getString(R.styleable.MyCustomStyleable_attr2)

  上面的这个调用都是放在控件的构造函数中,并且在theme.xml中有设置对应的customViewAttr属性
  4、参数 defStyleRes
这个参数起作用是在没设置前面三个的情况下,设置在方法obtainStyledAttributes中的第四个参数中,例如下

var t = con.obtainStyledAttributes(attrs, R.styleable.MyCustomStyleable,0,R.style.MyStyleDef)
val att1 = t.getString(R.styleable.MyCustomStyleable_attr1)
val att2 = t.getString(R.styleable.MyCustomStyleable_attr2)

  通过这例子应该知道使用了,但是怎么实现的,就需要看下源代码了。下篇文章Android TypedArray简单分析(二)源代码分析讲一下如何实现。

你可能感兴趣的:(android)