Android中的 Attr、Style、Theme区别



一、引言

Android开发经常会遇到Attr、Style、Theme这三个词,对于这三个概率只知道一些基本的使用,却但多少有一些迷惑,总的说来Attr、Style、Theme都是用来表达样式风格的,只是范围不一样。这三个概念贯穿Android框架的方方面面,是Android程序设计中很重要的一环,理解它们,不但可以让你的代码变得简洁明了,还可以让的应用更加灵活。

控件的属性的设置的五种方式(优先级基本是从上到下):

  • 直接在xml中直接定义
  • 通过Style来引用外部样式
  • 在对应的主题中定义外部Style引用(控件的第三个参数defStyleAttr如:textViewStyle,buttonStyle,preferenceStyle等等)
  • 在对应的默认style定义(也就是控件的第四个参数defStyleRes,一般都为0,也就是没有)二
  • 在对应的主题直接定义

二、定义

关于这三个概念的定义:

  • Attr:属性,风格样式的最小单元
  • Style:风格,它是一系列Attr的集合用以定义一个View的样式,比如height、width、padding等
  • Theme:主题,它与Style作用一样,不同于Style作用于个一个单独View,而它是作用于Activity上或是整个应用

三、Attr使用

平时开发中,只有在自定义View时才会定义Attr;在values目录下创建一个attrs.xml文件,在元素里面首先申明一个自己的表示一个属性组,再在里面加上属性就行。format表示了Attr的类型,可取的值有以下类型:

  • color:颜色值,如#000000
  • reference:引用某一资源ID。如@drawable/xxx
  • boolean:布尔值,true或false
  • dimension:尺寸值,可以为wrap_content、match_parent或是具体大小(xx dp)
  • float:浮点型
  • fraction:百分数
  • integer:整型
  • string:字符串类型
  • enum:枚举类型,各个取值互斥
  • flag:标记位,各个取值可用“|”连接

reference用在一些可以设置引用值的情况,比如@drawable/myImage、@color/myColor等。当然format也可以进行或运算,一般我们定义color类型的属性时,也一般会把format写成format=”reference|color”,这样我们不但可以设置颜色值,如#FFFFFF,还可以使用我们自己定义的demo图片,如@drawable/demo_pic。

    <declare-styleable name="SwitchView">
        <attr name="hasShadow" format="boolean"/>
        <attr name="primaryColor" format="color"/>
        <attr name="primaryColorDark" format="color"/>
        <attr name="isOpened" format="boolean"/>
    </declare-styleable>

format即使用错,只要你自定义的View中获取对应类型值也是可以的,只是在布局中写代码时,IDE就不会根据你定义的format给出相应的提示了,所以最好在自定义View时还是仔细斟酌下类型。

一种常见的在自定义View类中获取Attr的代码是这样的:

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwitchView);
        colorPrimary = a.getColor(R.styleable.SwitchView_primaryColor, DEFAULT_COLOR_PRIMARY);
        colorPrimaryDark = a.getColor(R.styleable.SwitchView_primaryColorDark, DEFAULT_COLOR_PRIMARY_DARK);
        hasShadow = a.getBoolean(R.styleable.SwitchView_hasShadow, true);
        isOpened = a.getBoolean(R.styleable.SwitchView_isOpened, false);
        state = isOpened ? STATE_SWITCH_ON : STATE_SWITCH_OFF;
        lastState = state;
        a.recycle();

四、Style使用

Style使用需要先配置参数,在values/styles.xml的 <resources>标签内增加如下内容:

    <style name="LoadSeekBar" parent="@android:style/Widget.SeekBar">
        <item name="android:progressDrawable">@drawable/bg_load_seekbar</item>
        <item name="android:thumb">@null</item>
        <item name="android:minHeight">2dp</item>
        <item name="android:maxHeight">2dp</item>
    </style>

    <style name="nameStyle">
        <item name="android:textColor">#000</item>
        <item name="android:textSize">18dp</item>
    </style>

如此一来,我们便定义了名为LoadSeekBar、nameStyle的Style,接下来看看如何使用它。

            <ProgressBar
                android:id="@+id/pb_update"
                style="@style/LoadSeekBar"
                android:layout_width="match_parent"
                android:layout_height="5dp"
                android:gravity="center"
                android:progress="0" />
    mfileName.setTextAppearance(mContext, R.style.nameStyle);

五、Theme使用

自定义Theme的方法与自定义Style相同,Theme与Style使用同一个元素标签 <style>区别在于所包含的属性不同,并且使用的地方也不一样。Theme需要设置到AndroidManifest.xml的 <application>或者 <activity> 标签下,设置后,被设置的Activity或整个应用下所有的View都可以使用该 <style> 里面的属性了。

 <style name="StartAppTheme" parent="AppTheme">
        <item name="android:windowBackground">@drawable/ic_bg_splash</item>
 </style>

有一点需要注意的是,若想把自定义的Theme设置给Activity或Application,需要让的自定义Theme继承自一个系统Theme,否则会抛出IllegalStateException异常。

    <activity
        android:name=".activity.SplashActivity"
        android:screenOrientation="portrait"
        android:theme="@style/StartAppTheme">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>

            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>

一个 Theme 实际上就是一个 Style,唯一的不同是他们在哪里被使用:在 Android Manifest 中为 APP 或者 activity 设置 Theme;在布局文件中为 widget 设置 Style。

六、总结

随着学习的深入,越发觉得这三块内容真是Android框架的一大神器,有时不用改动代码,只要换一个theme,应用马上焕发青春。而且也尝试用所学内容去写自己的Theme,不但可以让自己的布局文件更加清晰明了,而且还让自己的代码具有更高的扩展性,真是好处多多。


Q&A

如果想继承某个主题并修改某些属性,如何知道有哪些属性可以修改
A: 从源文件中找,如themes.xml

如何知道内置了哪些style
A: 源文件styles.xml, 或官方文档 R.style

如何知道一个View有哪些属性可用
A: 源文件attrs.xml中搜索view名称,或参考该View的官方文档

查看某主题的外观效果
A: 可以Theme Editor中查看