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属性的设置,如下
我们点进去看下AppTheme和AppTheme.NoActionBar
看到没有其实就是一个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中可以找到这个分组
看到没有,其中declare-styleable标签就表示对属性分组,name指定分组名,所以我们想要的就是上图中textAppearance属性值,而该属性我们没有在布局文件中指定所以在AttributeSet是找不到的,我们先假设第三,第四个参数不设置的情况,这是就会从当前主题中找,我就来看看AppTheme.NoActionBar的那个继承链中对textAppearance属性的最后一次设置(Style介绍中讲了,后设置的会覆盖先设置的属性值)最后在name为”Theme“的style也就是所有主题的跟主题中找到
至此,35行中的对TypeArray对象a中就封装有textAppearance属性,从以上两个图可得着textAppearance属性是引用类型,并引用着textAppearance这个style,们再来看看textAppearance这个style设置了些什么属性值
ok,图先放着,看回TextView的源码部分,代码39行获取属性值也就上图TextAppearance这个样式的id。拿到之后做一个判断,当然这里不为空来的43行调用了obtainStyledAttributes的另一个重载的函数,并把TextAppearance样式的id传入,这里是就是从TextAppearance样式中获取id为com.android.internal.R.styleable.TextAppearance的属性组的属性值,再从源码的attr.xml文件找一下TextAppearance属性组
其他属性就先不理了,只看textColor这个,在TextAppearance样式中确实对textColor进行了设置设置为?textColorPrimary,问号的意思表示引用当前主题下textColorPrimary的属性值,所我们再去AppTheme.NoActionBar的继承链中找一下textColorPrimary的设置结果发现在Platform.AppCompat.Light中找到了textColorPrimary
所以39行中的appearance最终封装了一组属性其中包括textColor属性,其值为colorPrimary(android预定义的颜色值) ,接下来看注解4其实就是遍历appearance里面的属性值然后第二个case把textColor属性值放到textColor变量中。
上面我们分析了第三和第四个参数不传的情况