1.style介绍
    style是android资源之一,放在res\values目录下的后缀名为.xml的文件中。Sylte的作用的是可以批量对控件属性进行设置,包括android预定义的属性和自定义属性。话不多说,我们先来看看代码

<!--style.xml-->
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="myTextViewStyle" >
        <item name="android:textColor" >#ffffff</item>
        <item name="android:textSize">50dp</item>
        <item name="android:background">#0000ff</item>
    </style>

    <style name="myTextViewStyle1" parent="myTextViewStyle">
        <item name="android:textSize">30dp</item>
    </style>

</resources>

    以上是我在values文件夹下新建的的一个style.xml文件,其中根标签必须为resources,表明他的子标签是资源类型。style标签定可以一个sytle资源,有连个属性name和parent,name指定该style的name,每个style的name是唯一的,parent指定sytle的父sytle。这里我定义了myTextViewStyle,myTextViewStyle1两个style,myTextViewStyle设定了字体颜色白色,大小50dp,背景颜色蓝色,而myTextViewStyle1继承了myTextViewStyle所设定的值并将大小改为30dp,子样式也可以添加另外的属性设置。然后我们就可以像这样在layout文件中使用了

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="myTextView"
        style="@style/myTextViewStyle"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="myTextView1"
        style="@style/myTextViewStyle1"/>

</LinearLayout>

    这样我们在要对多个控件设置同样属性值的使用就不用在layout文件中多次设置,只要把对应属性设置定义为一种style即可,然后把style设置给对应控件的style属性即可。在android framework中预定义也有很多的style,下面是我复制的一段源码

<style name="Base.Widget.AppCompat.Button" parent="android:Widget">
      <item name="android:background">@drawable/abc_btn_default_mtrl_shape</item>
      <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
      <item name="android:minHeight">48dip</item>
      <item name="android:minWidth">88dip</item>
      <item name="android:focusable">true</item>
      <item name="android:clickable">true</item>
      <item name="android:gravity">center_vertical|center_horizontal</item>
</style>

    以上的Base.Widget.AppCompat.Button是android用于button的样式,我们可以继承他并把android:background改为红色如下

<style name="redButton" parent="Base.Widget.AppCompat.Button">
     <item name="android:background">#ff0000</item>
</style>

注:安卓官方建议在继承自定义的style是不要用parent属性指定而是用[父样式] + . + [子样式的name属性]如上文的myTextViewStyle1样式应该这样创建

<style name="myTextViewStyle.myTextViewStyle1">
  <item name="android:textSize">30dp</item>
</style>

2.Theme介绍

    Theme本质上就是一种style资源,不同的是Theme是用于Activity,Dialog,或者Application中的。换句话说,被用与Activity,Dialog,或者Application中的style资源就称之为Theme。平时我们在AndroidMenifest.xml文件中就会看到Activity,Application的Theme属性的设置,如下

android style填充 安卓style属性_android


我们点进去看下AppTheme和AppTheme.NoActionBar

android style填充 安卓style属性_android_02


    看到没有其实就是一个style并继承其他android预定义的Theme,当然其他android预定义的Theme也都是一些style。application的theme设定了application下的所有属性的默认值,同理activity的Theme也设定了activity下所有属性的默认值且优先级高于application。这里说的属性是android预定义属性,例如我们常见的background,textColor等等。下面我来看看android源码是如何拿到Theme的属性值设置给ui的

3Theme源码
    有与预定义的属性有很多,ui控件也很多,这里我就以TextView为例,我们都知道Button在默认情况下有颜色的,那我们就来看看它的textColor属性和background属性是如何被设置的。
先来看TextView的构造函数

/*
package android.widget;
TextView.java
*/

 public TextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.textViewStyle);
  }

  public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
      this(context, attrs, defStyleAttr, 0);
  }

  @SuppressWarnings("deprecation")
  public TextView(
          Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
      super(context, attrs, defStyleAttr, defStyleRes);

      mText = "";

      final Resources res = getResources();
     ....
     ....
		//获取当前主题,也就就是上文中AppTheme.NoActionBar
      final Resources.Theme theme = context.getTheme();

      /*
       * Look the appearance up without checking first if it exists because
       * almost every TextView has one and it greatly simplifies the logic
       * to be able to parse the appearance first and then let specific tags
       * for this View override it.
       */
       
      TypedArray a = theme.obtainStyledAttributes(attrs,
              com.android.internal.R.styleable.TextViewAppearance, defStyleAttr, defStyleRes);
      TypedArray appearance = null;
      
      int ap = a.getResourceId(com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1);
      a.recycle();
      if (ap != -1) {
	      
          appearance = theme.obtainStyledAttributes(
                  ap, com.android.internal.R.styleable.TextAppearance);
      }
      if (appearance != null) {
         
          int n = appearance.getIndexCount();
          for (int i = 0; i < n; i++) {
              int attr = appearance.getIndex(i);
              switch (attr) {
              case com.android.internal.R.styleable.TextAppearance_textColorHighlight:
                  textColorHighlight = appearance.getColor(attr, textColorHighlight);
                  break;

              case com.android.internal.R.styleable.TextAppearance_textColor:
                  textColor = appearance.getColorStateList(attr);
                  break;
                ...............
       a = theme.obtainStyledAttributes(
                    attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);

        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);

            switch (attr) {
            case com.android.internal.R.styleable.TextView_editable:
                editable = a.getBoolean(attr, editable);
                break;

            case com.android.internal.R.styleable.TextView_inputMethod:
                inputMethod = a.getText(attr);
                break;

看下34行处的obtainStyledAttributes()函数,该函数的作用就是来获取属性值的。第一参数是attr,就是构造函数函数传过来的AttributeSet 对象,该对象包含了我们在布局文件中有设置的属性值(注意是有设置才有),第二个函数是想要获取的属性组,就是想要从AttributeSet 中拿到那些属性值,第三参数是默认属性,如果在AttributeSet 没找到想要的属性值就会到该属性所设置的值去寻找,第四个参数是默认样式,如果上面的方法都没找想要的属性值就会从该样式中去找(我们都知道样式就是属性的批量设置),如果都找不到就会从当前主题中去找,最后把得到的属性封装到TypedArray 对象中。这里第二个参数传的是com.android.internal.R.styleable.TextViewAppearance,这就是我们想获取的属性组id,在android源码的attr.xml中可以找到这个分组

android style填充 安卓style属性_android style填充_03


看到没有,其中declare-styleable标签就表示对属性分组,name指定分组名,所以我们想要的就是上图中textAppearance属性值,而该属性我们没有在布局文件中指定所以在AttributeSet是找不到的,我们先假设第三,第四个参数不设置的情况,这是就会从当前主题中找,我就来看看AppTheme.NoActionBar的那个继承链中对textAppearance属性的最后一次设置(Style介绍中讲了,后设置的会覆盖先设置的属性值)最后在name为”Theme“的style也就是所有主题的跟主题中找到

android style填充 安卓style属性_xml_04


至此,35行中的对TypeArray对象a中就封装有textAppearance属性,从以上两个图可得着textAppearance属性是引用类型,并引用着textAppearance这个style,们再来看看textAppearance这个style设置了些什么属性值

android style填充 安卓style属性_android_05


ok,图先放着,看回TextView的源码部分,代码39行获取属性值也就上图TextAppearance这个样式的id。拿到之后做一个判断,当然这里不为空来的43行调用了obtainStyledAttributes的另一个重载的函数,并把TextAppearance样式的id传入,这里是就是从TextAppearance样式中获取id为com.android.internal.R.styleable.TextAppearance的属性组的属性值,再从源码的attr.xml文件找一下TextAppearance属性组

android style填充 安卓style属性_resources_06


其他属性就先不理了,只看textColor这个,在TextAppearance样式中确实对textColor进行了设置设置为?textColorPrimary,问号的意思表示引用当前主题下textColorPrimary的属性值,所我们再去AppTheme.NoActionBar的继承链中找一下textColorPrimary的设置结果发现在Platform.AppCompat.Light中找到了textColorPrimary

android style填充 安卓style属性_resources_07


所以39行中的appearance最终封装了一组属性其中包括textColor属性,其值为colorPrimary(android预定义的颜色值) ,接下来看注解4其实就是遍历appearance里面的属性值然后第二个case把textColor属性值放到textColor变量中。

    上面我们分析了第三和第四个参数不传的情况