Android开发Style和Theme精准知识面总结,看到的赶紧收藏!

本文详细介绍了Android中style和theme的使用,包括资源位置、自定义属性声明、属性值获取、属性加载优先级、style的使用、style的继承、theme的使用以及版本兼容。还探讨了@android、?attr/和?android的区别,为开发者提供了一个全面的Android样式和主题知识总结。
摘要由CSDN通过智能技术生成

这块知识大部分同学都知道,但是同样比较碎,比如说定义一个Style,到底该用于主题还是用于某个view?再比如说Style的继承应该怎么用?等等…本文我将尽可能全面的将这块知识点总结一下。

资源位置

开发过程中style和theme等这些资源文件会放在res/values/文件夹下,都是xml文件。

  • attrs.xml
  • colors.xml
  • dimens.xml
  • string.xml
  • arrays.xml
  • styles.xml
  • themes.xml

以上这些xml文件项目中没有的话可以新建,xml命名一般会在结尾带一个’s’,表示里面的内容不止一个。另外命名也可以根据项目模块加以区分,例如main_strings.xml,更容易管理。

attrs.xml

该文件主要定义一些自定义view的属性声明

属性声明

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="attr_str" format="string"></attr>
    <attr name="attr_bool" format="boolean"></attr>
    <attr name="attr_int" format="integer"></attr>
    <attr name="attr_ref" format="reference"></attr>
</resources>

其中 name表示属性名,format表示其接受的输入格式。format还有其它格式,如:

  • color – 颜色值
  • dimension – 尺寸
  • float – 浮点值
  • fraction – 百分比
  • enum – 枚举
  • flag – 位或运算
  • 混合类型 – 多种format结合

enum和flag声明(可以不指定format),如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="attr_enum">
        <enum name="first" value="1"></enum>
        <enum name="second" value="2"></enum>
        <enum name="third" value="3"></enum>
    </attr>

    <attr name="attr_flag">
        <flag name="east" value="0x1"></flag>
        <flag name="west" value="0x2"></flag>
        <flag name="south" value="0x3"></flag>
        <flag name="north" value="0x4"></flag>
    </attr>
</resources>

开发过程中自定义view的属性声明并不采用在<resources/>中直接声明<attr/>的形式,因为一个attrs.xml中可以有多个view的属性声明,可采用declare-styleable声明每个view的属性组(TypedArray):

<resources>
    <declare-styleable name="MyStyleable">
        <attr name="attr_str" format="string"></attr>
        <attr name="attr_bool" format="boolean"></attr>
        <attr name="attr_int" format="integer"></attr>
        <attr name="attr_ref" format="reference"></attr>
    </declare-styleable>
</resources>

一个attrs.xml文件中有多个view共有的属性我们可以抽取出来:

<resources>
    //共有的attr_ref属性抽取出来
    <attr name="attr_ref" format="reference"></attr>

    //view0
    <declare-styleable name="MyStyleable0">
        <attr name="attr_str" format="string"></attr>
        <attr name="attr_bool" format="boolean"></attr>
        <attr name="attr_int" format="integer"></attr>
        <attr name="attr_ref"/>
    </declare-styleable>

    //view1
    <declare-styleable name="MyStyleable0">
        <attr name="attr_str" format="string"></attr>
        <attr name="attr_bool" format="boolean"></attr>
        <attr name="attr_int" format="integer"></attr>
        <attr name="attr_ref"/>
    </declare-styleable>
</resources>

属性值获取

xml中设定值:

<com.myapplication.attr.MyAttrView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:attr_str="hello world"
    app:attr_bool="true"
    app:attr_int="99"
    app:attr_ref="@dimen/dp_100"
    >
</com.myapplication.attr.MyAttrView>

代码中解析值:

public MyView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);

    //R.styleable.MyStyleable 指的是想要解析的属性
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyStyleable);
    String strValue = a.getString(R.styleable.MyStyleable_attr_str);
    boolean boolValue = a.getBoolean(R.styleable.MyStyleable_attr_bool, false);
    int intValue = a.getInt(R.styleable.MyStyleable_attr_int, 0);
    float refValue = a.getDimension(R.styleable.MyStyleable_attr_ref, 0);

    //typedArray 存放在缓存池,因此用完归还到缓存池
    a.recycle();
}

自定义属性加载优先级

自定义属性值的方式目前可以归总为三种:

  1. 在布局文件里定义属性
  2. 在style里定义属性
  3. 在theme里定义属性

我们知道自定义view中有四个构造方法,其中第四个构造方法是

public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)

其中后两个入参:

  • int defStyleAttr 默认属性
  • int defStyleRes 默认style

因此view的属性可能来自以下5个地方:

  1. 在布局文件里定义属性。
  2. 在style里定义属性。
  3. 在theme里定义属性。
  4. 默认的属性。
  5. 默认的style。

根据越精细化指定优先级越高,那么优先级从低到高排列也应该是上边5个地方的顺序了。

自定义属性加载流程

自定义属性加载流程

styles.xml

它是view中一些列属性值的集合,比如height、width、padding等,包括自定义attr属性。

style的使用

<resources>
    <style name="myStyle">
        <item name="attr_str">hello world</item>
        <item name="attr_bool">true</item>
        <item name="android:background">@android:color/transparent</item>
    </style>
</resources>
<com.myapplication.attr.MyAttrView
    style="@style/myStyle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
</com.myapplication.attr.MyAttrView>
等价于
<com.myapplication.attr.MyAttrView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    app:attr_str="hello world"
    app:attr_bool="true">
</com.myapplication.attr.MyAttrView>

style的继承

Style的继承主要分两种:

  • 指定parent继承,主要是第三方module的Style(包括系统内建的或者其他lib)
  • 点式继承,主要是自己本项目中的style

继承第三方module的Style:

//继承系统内建的style(继承Framework中theme的属性是需要“android:”开头)
<style name="GreenText" parent="@android:style/TextAppearance">
    <item name="android:textColor">#00FF00</item>
</style>

//继承自AppCompat下的style(继承Support Library中theme的属性是不需要“android:”开头的)
<style name="GreenText" parent="TextAppearance.AppCompat">
    <item name="android:textColor">#00FF00</item>
</style>

//继承自其他lib的style
<style name="GreenText" parent="@style/BaseGreenText">
    <item name="android:textColor">#00FF00</item>
</style>

继承自己本项目中的style:

<style name="GreenText.Large">
    <item name="android:textSize">22dp</item>
</style>

继承自己本项目中的style可以不用parent属性指定,但是如果在此种方法中也使用parent,parent中指定的style优先级高于通过点操作符指定的父类style。

themes.xml

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

theme的使用

//定义主题
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

//application指定
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

theme中可以自定义很多属性,具体可以查阅官方R.styleable.Theme

定义全局View默认样式

如果是要在theme中定义全局的View或具体控件的属性则需要查看其支持的xml属性配置:

<style name="MyTheme" parent="AppTheme">
    <!--重写系统view的style默认样式-->
    <item name="android:buttonStyle">@style/myButton</item>
    <item name="android:checkboxStyle">@style/myCheckBox</item>
</style>

<style name="myButton" parent="@style/Base.Widget.AppCompat.Button">
    <item name="android:background">@color/colorAccent</item>
    <item name="android:textColor">@color/white</item>
    <item name="android:textAppearance">@style/Base.TextAppearance.AppCompat.Small</item>
</style>

<style name="myCheckBox" parent="@style/Base.Widget.AppCompat.CompoundButton.CheckBox">
    <item name="android:background">@color/colorPrimaryDark</item>
    <item name="android:textColor">@color/white</item>
    <item name="android:textAppearance">@style/Base.TextAppearance.AppCompat.Display1</item>
    <item name="android:checked">true</item>
</style>

Material Color system

如果使用的是Material Theme,它提供了很多的 color attribute 使用:

  • colorPrimary : 顧名思義,就是主要的顏色,這個通常指得是 App 本身產品的代表色,通常也是品牌的主要視覺色
  • colorPrimaryVariant:主要顏色的變體,通常會從 colorPrimary 往較淡或較濃的色澤
  • colorOnPrimary:字面意思就是主要顏色上頭的顏色,這個顏色通常使用在背景色是主要顏色的元件上頭(例如字樣 Label 、icon 等)
  • colorSecondary:app 次要的品牌顏色,這些用於裝飾某些特定需要的 widget
  • colorSecondaryVariant:次要顏色的變體,也就是次要顏色偏暗或偏亮的樣式
  • colorOnSecondary:用於顯示於次要顏色上元件的顏色
  • colorError:顯示錯誤的顏色 (最常見的就是紅色)
  • colorOnError:在錯誤顏色上頭元件的顏色
  • colorSurface:表層顏色(就是 Sheet 的顏色)
  • colorOnSurface:在表層顏色上的的元件顏色
  • android:colorBackground:最底的背景色
  • colorOnBackground:用於對底背景色上頭的元件用的顏色

利用这些属性,搭配上面的那些技巧,可以組合出很棒的效果。

Material_Color_system图示

版本兼容

随着android版本升级,不同版本下的api可能不一样,可以通过在res文件夹下创建指定values版本的style来解决兼容问题:

res/values/styles.xml        # 如果没有指定版本,默认使用此style中的定义
res/values-v21/styles.xml    # 对于Api 21以上的版本会优先使用此style种的定义

举个例子,比如Android5.0(API level 21)及以上版本汇总,增加了window的切换动画。

默认res/values/styles.xml

<resources>
    <!-- base set of styles that apply to all versions -->
    <style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/primaryColor</item>
        <item name="colorPrimaryDark">@color/primaryTextColor</item>
        <item name="colorAccent">@color/secondaryColor</item>
    </style>

    <!-- declare the theme name that's actually applied in the manifest file -->
    <style name="AppTheme" parent="BaseAppTheme" />
</resources>

Api 21的版本res/values-v21/styles.xml

<resources>
    <!-- extend the base theme to add styles available only with API level 21+ -->
    <style name="AppTheme" parent="BaseAppTheme">
        <item name="android:windowActivityTransitions">true</item>
        <item name="android:windowEnterTransition">@android:transition/slide_right</item>
        <item name="android:windowExitTransition">@android:transition/slide_left</item>
    </style>
</resources>

这种方式还可用于处理其他资源的不同版本兼容问题。

@android, ?attr/ 和 ?android 的区别

开发过程中经常遇到"@android"、"?attr/“或者”?android"。例如,设置一些可点击组件的波纹效果时,我们会用到:android:foreground="?attr/selectableItemBackground"。有同学可能就懵逼了,到底什么时候用"@android"什么时候用"?attr/"呢?

其实@?的区别是:

  • @:引用固定的系统资源
  • ?: 引用attr指定属性资源

最为常见的引用系统固定资源格式如下:

@[<package_name>:]<resource_type>/<resource_name>

这种方式是最为常见的,直接获取对应的包下的资源,一般在相同的包下,可以省略包名,比如为 TextView 设置文字时,就可以通过这样的方式来获取我们应用内定义的 string 资源:

android:text="@string/hello"

@android: 引用安卓内建的系统资源

android:background=“@android:drawable/ic_menu_delete”

对应的是当前compileSdkVersion版本下的android系统下的固定资源。

?attr/ 引用应用内的属性资源

通常我们会在 values/ 文件夹下建一个 attrs 文件,在这里保存一些我们自己的 style 属性,其实这些属性就可以通过 ?attr/ 这种方式来引用了:

//attrs文件中定义
<attr name="colorReallyGreen" format="color"/>
...

//view中引用
android:background="?attr/colorReallyGreen"

当然,要想让该属性起作用还需要在style或者theme中下指定值:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    ...
    <item name="colorReallyGreen">@color/colorReallyGreen</item>
</style>

由于在 layout 中,可以自动识别出当前所需的是属性资源,所以可以省略 attr/ 而直接使用 ?colorReallyGreen 就可以了。

?android: 引用系统内建的属性资源

与 ?attr/ 类似,通过这种方式可以直接访问到安卓内建的属性资源,只不过是省略了 attr/ 而已,比如给 TextView 引用一个系统内的 style buttonStyleSmall:

<TextView
    style="?android:buttonStyleSmall"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="You Are Beautiful" />

最后

以上是我针对自定义属性以及自定义style和自定义theme的相关知识点总结,这部分是自定义view的基础,内容还是比较多的,有一部分没有涉及到,例如主题切换,感兴趣的童鞋可以自行学习。

我是i猩人,喜欢本篇文章的童鞋欢迎点赞、关注。

参考

  • https://developer.android.com/guide/topics/ui/look-and-feel/themes?hl=zh-cn
  • https://medium.com/jastzeonic/style-theme-%E7%9A%84%E9%82%A3%E4%B8%80%E5%85%A9%E4%BB%B6%E4%BA%8B%E6%83%85-8499a6603bbb
  • https://mp.weixin.qq.com/s/Vo8MXlHF5ur2QsZxWAccIg
  • https://juejin.cn/post/6844904200673968141#heading-8
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值